Intel-XE Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/18] GPU debug support (eudebug) v2
@ 2024-10-01 14:42 Mika Kuoppala
  2024-10-01 14:42 ` [PATCH 01/18] ptrace: export ptrace_may_access Mika Kuoppala
                   ` (20 more replies)
  0 siblings, 21 replies; 40+ messages in thread
From: Mika Kuoppala @ 2024-10-01 14:42 UTC (permalink / raw)
  To: intel-xe; +Cc: Mika Kuoppala

Hi,

This is a continuation of the first submission for GPU debug support:

https://lists.freedesktop.org/archives/intel-xe/2024-July/043605.html

The most significant changes are:
  1. Placing the code behind Kconfig switch

  2. Using rw_semaphore to allow resource discovery to run without
     individual resource locks.

The page fault handling RFC will be based on this series but will be
posted separately.

Thanks to all contributors for their feedback on v1 and to patch
authors for their fixes. I hope I haven't missed anything.

Mika

Andrzej Hajda (1):
  drm/xe/eudebug: implement userptr_vma access

Christoph Manszewski (3):
  drm/xe/eudebug: Add vm bind and vm bind ops
  drm/xe/eudebug: Dynamically toggle debugger functionality
  drm/xe/eudebug_test: Introduce xe_eudebug wa kunit test

Dominik Grzegorzek (8):
  drm/xe/eudebug: Introduce exec_queue events
  drm/xe/eudebug: hw enablement for eudebug
  drm/xe: Add EUDEBUG_ENABLE exec queue property
  drm/xe/eudebug: Introduce per device attention scan worker
  drm/xe/eudebug: Introduce EU control interface
  drm/xe: Debug metadata create/destroy ioctls
  drm/xe: Attach debug metadata to vma
  drm/xe/eudebug: Add debug metadata support for xe_eudebug

Mika Kuoppala (6):
  ptrace: export ptrace_may_access
  drm/xe/eudebug: Introduce eudebug support
  drm/xe/eudebug: Introduce discovery for resources
  drm/xe/eudebug: Add UFENCE events with acks
  drm/xe/eudebug: vm open/pread/pwrite
  drm/xe/eudebug: Implement vm_bind_op discovery

 drivers/gpu/drm/xe/Kconfig                   |   10 +
 drivers/gpu/drm/xe/Makefile                  |    4 +
 drivers/gpu/drm/xe/regs/xe_engine_regs.h     |    7 +
 drivers/gpu/drm/xe/regs/xe_gt_regs.h         |   43 +
 drivers/gpu/drm/xe/tests/xe_eudebug.c        |  175 +
 drivers/gpu/drm/xe/tests/xe_live_test_mod.c  |    5 +
 drivers/gpu/drm/xe/xe_debug_metadata.c       |  233 ++
 drivers/gpu/drm/xe/xe_debug_metadata.h       |   98 +
 drivers/gpu/drm/xe/xe_debug_metadata_types.h |   28 +
 drivers/gpu/drm/xe/xe_device.c               |   26 +-
 drivers/gpu/drm/xe/xe_device.h               |   36 +
 drivers/gpu/drm/xe/xe_device_types.h         |   47 +
 drivers/gpu/drm/xe/xe_eudebug.c              | 3891 ++++++++++++++++++
 drivers/gpu/drm/xe/xe_eudebug.h              |   93 +
 drivers/gpu/drm/xe/xe_eudebug_types.h        |  328 ++
 drivers/gpu/drm/xe/xe_exec.c                 |    2 +-
 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_execlist.c             |    2 +-
 drivers/gpu/drm/xe/xe_gt_debug.c             |  148 +
 drivers/gpu/drm/xe/xe_gt_debug.h             |   27 +
 drivers/gpu/drm/xe/xe_hw_engine.c            |    1 +
 drivers/gpu/drm/xe/xe_lrc.c                  |   16 +-
 drivers/gpu/drm/xe/xe_lrc.h                  |    4 +-
 drivers/gpu/drm/xe/xe_reg_sr.c               |   21 +-
 drivers/gpu/drm/xe/xe_reg_sr.h               |    4 +-
 drivers/gpu/drm/xe/xe_rtp.c                  |    2 +-
 drivers/gpu/drm/xe/xe_sync.c                 |   45 +-
 drivers/gpu/drm/xe/xe_sync.h                 |    8 +-
 drivers/gpu/drm/xe/xe_sync_types.h           |   28 +-
 drivers/gpu/drm/xe/xe_vm.c                   |  173 +-
 drivers/gpu/drm/xe/xe_vm.h                   |    3 +
 drivers/gpu/drm/xe/xe_vm_types.h             |   40 +
 drivers/gpu/drm/xe/xe_wa_oob.rules           |    2 +
 include/uapi/drm/xe_drm.h                    |   96 +-
 include/uapi/drm/xe_drm_eudebug.h            |  225 +
 kernel/ptrace.c                              |    1 +
 38 files changed, 5887 insertions(+), 50 deletions(-)
 create mode 100644 drivers/gpu/drm/xe/tests/xe_eudebug.c
 create mode 100644 drivers/gpu/drm/xe/xe_debug_metadata.c
 create mode 100644 drivers/gpu/drm/xe/xe_debug_metadata.h
 create mode 100644 drivers/gpu/drm/xe/xe_debug_metadata_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_types.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 include/uapi/drm/xe_drm_eudebug.h

-- 
2.34.1


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

* [PATCH 01/18] ptrace: export ptrace_may_access
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
@ 2024-10-01 14:42 ` Mika Kuoppala
  2024-10-01 14:42 ` [PATCH 02/18] drm/xe/eudebug: Introduce eudebug support Mika Kuoppala
                   ` (19 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Mika Kuoppala @ 2024-10-01 14:42 UTC (permalink / raw)
  To: intel-xe
  Cc: Mika Kuoppala, Oleg Nesterov, linux-kernel, Dave Airlie,
	Lucas De Marchi, Matthew Brost, Andi Shyti, Joonas Lahtinen,
	Maciej Patelczyk, Dominik Grzegorzek, Jonathan Cavitt, Andi Shyti

xe driver would like to allow fine grained access control
for GDB debugger using ptrace. Without this export, the only
option would be to check for CAP_SYS_ADMIN.

The check intended for an ioctl to attach a GPU debugger
is similar to the ptrace use case: allow a calling process
to manipulate a target process if it has the necessary
capabilities or the same permissions, as described in
Documentation/process/adding-syscalls.rst.

Export ptrace_may_access function to allow GPU debugger to
have identical access control for debugger(s)
as a CPU debugger.

v2: proper commit message (Lucas)

Cc: Oleg Nesterov <oleg@redhat.com>
Cc: linux-kernel@vger.kernel.org
Cc: Dave Airlie <airlied@redhat.com>
CC: Lucas De Marchi <lucas.demarchi@intel.com>
Cc: Matthew Brost <matthew.brost@intel.com>
CC: Andi Shyti <andi.shyti@intel.com>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
CC: Maciej Patelczyk <maciej.patelczyk@linux.intel.com>
Cc: Dominik Grzegorzek <dominik.grzegorzek@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Signed-off-by: Jonathan Cavitt <jonathan.cavitt@intel.com>
Reviewed-by: Andi Shyti <andi.shyti@linux.intel.com>
---
 kernel/ptrace.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index d5f89f9ef29f..86be1805ebd8 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -354,6 +354,7 @@ bool ptrace_may_access(struct task_struct *task, unsigned int mode)
 	task_unlock(task);
 	return !err;
 }
+EXPORT_SYMBOL_GPL(ptrace_may_access);
 
 static int check_ptrace_options(unsigned long data)
 {
-- 
2.34.1


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

* [PATCH 02/18] drm/xe/eudebug: Introduce eudebug support
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
  2024-10-01 14:42 ` [PATCH 01/18] ptrace: export ptrace_may_access Mika Kuoppala
@ 2024-10-01 14:42 ` Mika Kuoppala
  2024-10-01 14:42 ` [PATCH 03/18] drm/xe/eudebug: Introduce discovery for resources Mika Kuoppala
                   ` (18 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Mika Kuoppala @ 2024-10-01 14:42 UTC (permalink / raw)
  To: intel-xe
  Cc: Mika Kuoppala, Maarten Lankhorst, Lucas De Marchi,
	Dominik Grzegorzek, Andi Shyti, Matt Roper, Matthew Brost,
	Zbigniew Kempczyński, Andrzej Hajda, Maciej Patelczyk,
	Jonathan Cavitt

With eudebug event interface, user space debugger process (like gdb)
is able to keep track of resources created by another process
(debuggee using drm/xe) and act upon these resources.

For example, debugger can find a client vm which contains isa/elf
for a particular shader/eu-kernel and then inspect and modify it
(for example installing a breakpoint).

Debugger first opens a connection to xe with a drm ioctl specifying
target pid to connect. This returns an anon fd handle that can then be
used to listen for events with dedicated ioctl.

This patch introduces eudebug connection and event queuing, adding
client create/destroy and vm create/destroy events as a baseline.
More events for full debugger operation are needed and
those will be introduced in follow up patches.

The resource tracking parts are inspired by the work of
Maciej Patelczyk on resource handling for i915. Chris Wilson
suggested improvement of two ways mapping which makes it easy to
use resource map as a definitive bookkeep of what resources
are played to debugger in the discovery phase (on 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)

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>
---
 drivers/gpu/drm/xe/Kconfig            |   10 +
 drivers/gpu/drm/xe/Makefile           |    2 +
 drivers/gpu/drm/xe/xe_device.c        |   11 +
 drivers/gpu/drm/xe/xe_device_types.h  |   28 +
 drivers/gpu/drm/xe/xe_eudebug.c       | 1114 +++++++++++++++++++++++++
 drivers/gpu/drm/xe/xe_eudebug.h       |   46 +
 drivers/gpu/drm/xe/xe_eudebug_types.h |  169 ++++
 drivers/gpu/drm/xe/xe_vm.c            |    7 +-
 include/uapi/drm/xe_drm.h             |   21 +
 include/uapi/drm/xe_drm_eudebug.h     |   56 ++
 10 files changed, 1463 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 7bbe46a98ff1..23f34e8e3151 100644
--- a/drivers/gpu/drm/xe/Kconfig
+++ b/drivers/gpu/drm/xe/Kconfig
@@ -85,6 +85,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 8f1c5c329f79..a3cd467cdaa6 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -113,6 +113,8 @@ xe-y += xe_bb.o \
 	xe_wa.o \
 	xe_wopcm.o
 
+xe-$(CONFIG_DRM_XE_EUDEBUG) += xe_eudebug.o
+
 xe-$(CONFIG_HMM_MIRROR) += xe_hmm.o
 
 # graphics hardware monitoring (HWMON) support
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index 8e9b551c7033..5615e2c23bf6 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -27,6 +27,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"
@@ -101,6 +102,8 @@ static int xe_file_open(struct drm_device *dev, struct drm_file *file)
 		put_task_struct(task);
 	}
 
+	xe_eudebug_file_open(xef);
+
 	return 0;
 }
 
@@ -159,6 +162,8 @@ static void xe_file_close(struct drm_device *dev, struct drm_file *file)
 
 	xe_pm_runtime_get(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
@@ -197,6 +202,7 @@ static const struct drm_ioctl_desc xe_ioctls[] = {
 	DRM_IOCTL_DEF_DRV(XE_WAIT_USER_FENCE, xe_wait_user_fence_ioctl,
 			  DRM_RENDER_ALLOW),
 	DRM_IOCTL_DEF_DRV(XE_OBSERVATION, xe_observation_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)
@@ -286,6 +292,8 @@ static void xe_device_destroy(struct drm_device *dev, void *dummy)
 {
 	struct xe_device *xe = to_xe_device(dev);
 
+	xe_eudebug_fini(xe);
+
 	if (xe->preempt_fence_wq)
 		destroy_workqueue(xe->preempt_fence_wq);
 
@@ -358,7 +366,10 @@ struct xe_device *xe_device_create(struct pci_dev *pdev,
 	INIT_LIST_HEAD(&xe->pinned.external_vram);
 	INIT_LIST_HEAD(&xe->pinned.evicted);
 
+	xe_eudebug_init(xe);
+
 	xe->preempt_fence_wq = alloc_ordered_workqueue("xe-preempt-fence-wq", 0);
+
 	xe->ordered_wq = alloc_ordered_workqueue("xe-ordered-wq", 0);
 	xe->unordered_wq = alloc_workqueue("xe-unordered-wq", 0, 0);
 	xe->destroy_wq = alloc_workqueue("xe-destroy-wq", 0, 0);
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index 85bede4dd646..cb4b52888a4b 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -386,6 +386,11 @@ struct xe_device {
 
 		/** @clients.count: number of drm clients */
 		u64 count;
+
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+		/** @xa: client list for eudebug discovery */
+		struct list_head list;
+#endif
 	} clients;
 
 	/** @usm: unified memory state */
@@ -524,6 +529,22 @@ struct xe_device {
 	u8 vm_inject_error_position;
 #endif
 
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+	/** @debugger connection list and globals for device */
+	struct {
+		/** @lock: protects the list of connections */
+		spinlock_t lock;
+		/** @list: list of connections, aka debuggers */
+		struct list_head list;
+
+		/** @session_count: session counter to track connections */
+		u64 session_count;
+
+		/** @available: is the debugging functionality available */
+		bool available;
+	} eudebug;
+#endif
+
 	/* private: */
 
 #if IS_ENABLED(CONFIG_DRM_XE_DISPLAY)
@@ -636,6 +657,13 @@ struct xe_file {
 
 	/** @refcount: ref count of this xe file */
 	struct kref refcount;
+
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+	struct {
+		/** @client_link: list entry in xe_device.clients.list */
+		struct list_head client_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..ea0cfd7697aa
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -0,0 +1,1114 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2023 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 "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 for_each_debugger_rcu(debugger, head) \
+	list_for_each_entry_rcu((debugger), (head), connection_link)
+#define for_each_debugger(debugger, head) \
+	list_for_each_entry((debugger), (head), connection_link)
+
+#define cast_event(T, event) container_of((event), typeof(*(T)), base)
+
+#define XE_EUDEBUG_DBG_STR "eudbg: %lld:%lu:%s (%d/%d) -> (%d/%d): "
+#define XE_EUDEBUG_DBG_ARGS(d) (d)->session, \
+		atomic_long_read(&(d)->events.seqno), \
+		READ_ONCE(d->connection.status) <= 0 ? "disconnected" : "", \
+		current->pid, \
+		task_tgid_nr(current), \
+		(d)->target_task->pid, \
+		task_tgid_nr((d)->target_task)
+
+#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__)
+
+#define struct_member(T, member) (((T *)0)->member)
+
+/* Keep 1:1 parity with uapi events */
+#define write_member(T_out, ptr, member, value) { \
+	BUILD_BUG_ON(sizeof(*ptr) != sizeof(T_out)); \
+	BUILD_BUG_ON(offsetof(typeof(*ptr), member) != \
+		     offsetof(typeof(T_out), member)); \
+	BUILD_BUG_ON(sizeof(ptr->member) != sizeof(value)); \
+	BUILD_BUG_ON(sizeof(struct_member(T_out, member)) != sizeof(value)); \
+	BUILD_BUG_ON(!typecheck(typeof((ptr)->member), value));	\
+	(ptr)->member = (value); \
+	}
+
+static struct xe_eudebug_event *
+event_fifo_pending(struct xe_eudebug *d)
+{
+	struct 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)
+{
+	int status;
+
+	spin_lock(&d->connection.lock);
+	status = d->connection.status;
+	spin_unlock(&d->connection.lock);
+
+	return status <= 0;
+}
+
+static int
+xe_eudebug_error(const struct xe_eudebug * const d)
+{
+	const int status = READ_ONCE(d->connection.status);
+
+	return status <= 0 ? status : 0;
+}
+
+static unsigned int
+event_fifo_has_events(struct xe_eudebug *d)
+{
+	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_resources * const res, const int t)
+{
+	return &res->rt[t];
+}
+
+static struct xe_eudebug_resources *
+xe_eudebug_resources_alloc(void)
+{
+	struct xe_eudebug_resources *res;
+	int err;
+	int i;
+
+	res = kzalloc(sizeof(*res), GFP_ATOMIC);
+	if (!res)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&res->lock);
+
+	for (i = 0; i < XE_EUDEBUG_RES_TYPE_COUNT; i++) {
+		xa_init_flags(&res->rt[i].xa, XA_FLAGS_ALLOC1);
+		err = rhashtable_init(&res->rt[i].rh, &rhash_res);
+
+		if (err)
+			break;
+	}
+
+	if (err) {
+		while (i--) {
+			xa_destroy(&res->rt[i].xa);
+			rhashtable_destroy(&res->rt[i].rh);
+		}
+
+		kfree(res);
+		return ERR_PTR(err);
+	}
+
+	return res;
+}
+
+static void res_free_fn(void *ptr, void *arg)
+{
+	XE_WARN_ON(ptr);
+	kfree(ptr);
+}
+
+static void
+xe_eudebug_destroy_resources(struct xe_eudebug *d)
+{
+	struct xe_eudebug_resources *res = d->res;
+	struct xe_eudebug_handle *h;
+	unsigned long j;
+	int i;
+	int err;
+
+	mutex_lock(&res->lock);
+	for (i = 0; i < XE_EUDEBUG_RES_TYPE_COUNT; i++) {
+		struct xe_eudebug_resource *r = &res->rt[i];
+
+		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(&res->lock);
+
+	for (i = 0; i < XE_EUDEBUG_RES_TYPE_COUNT; i++) {
+		struct xe_eudebug_resource *r = &res->rt[i];
+
+		rhashtable_free_and_destroy(&r->rh, res_free_fn, NULL);
+		xe_eudebug_assert(d, xa_empty(&r->xa));
+		xa_destroy(&r->xa);
+	}
+
+	mutex_destroy(&res->lock);
+
+	kfree(res);
+}
+
+static void xe_eudebug_free(struct kref *ref)
+{
+	struct xe_eudebug *d = container_of(ref, typeof(*d), ref);
+	struct xe_eudebug_event *event;
+
+	while (kfifo_get(&d->events.fifo, &event))
+		kfree(event);
+
+	xe_eudebug_destroy_resources(d);
+	put_task_struct(d->target_task);
+
+	xe_eudebug_assert(d, !kfifo_len(&d->events.fifo));
+
+	kfree_rcu(d, rcu);
+}
+
+static void xe_eudebug_put(struct xe_eudebug *d)
+{
+	kref_put(&d->ref, xe_eudebug_free);
+}
+
+static struct task_struct *find_get_target(const pid_t nr)
+{
+	struct task_struct *task;
+
+	rcu_read_lock();
+	task = pid_task(find_pid_ns(nr, task_active_pid_ns(current)), PIDTYPE_PID);
+	if (task)
+		get_task_struct(task);
+	rcu_read_unlock();
+
+	return task;
+}
+
+static int
+xe_eudebug_attach(struct xe_device *xe, struct xe_eudebug *d,
+		  const pid_t pid_nr)
+{
+	struct task_struct *target;
+	struct xe_eudebug *iter;
+	int ret = 0;
+
+	target = find_get_target(pid_nr);
+	if (!target)
+		return -ENOENT;
+
+	if (!ptrace_may_access(target, PTRACE_MODE_READ_REALCREDS)) {
+		put_task_struct(target);
+		return -EACCES;
+	}
+
+	XE_WARN_ON(d->connection.status != 0);
+
+	spin_lock(&xe->eudebug.lock);
+	for_each_debugger(iter, &xe->eudebug.list) {
+		if (!same_thread_group(iter->target_task, target))
+			continue;
+
+		ret = -EBUSY;
+	}
+
+	if (!ret && xe->eudebug.session_count + 1 == 0)
+		ret = -ENOSPC;
+
+	if (!ret) {
+		d->connection.status = XE_EUDEBUG_STATUS_CONNECTED;
+		d->xe = xe;
+		d->target_task = get_task_struct(target);
+		d->session = ++xe->eudebug.session_count;
+		kref_get(&d->ref);
+		list_add_tail_rcu(&d->connection_link, &xe->eudebug.list);
+	}
+	spin_unlock(&xe->eudebug.lock);
+
+	put_task_struct(target);
+
+	return ret;
+}
+
+static bool xe_eudebug_detach(struct xe_device *xe,
+			      struct xe_eudebug *d,
+			      const int err)
+{
+	bool detached = false;
+
+	XE_WARN_ON(err > 0);
+
+	spin_lock(&d->connection.lock);
+	if (d->connection.status == XE_EUDEBUG_STATUS_CONNECTED) {
+		d->connection.status = err;
+		detached = true;
+	}
+	spin_unlock(&d->connection.lock);
+
+	if (!detached)
+		return false;
+
+	spin_lock(&xe->eudebug.lock);
+	list_del_rcu(&d->connection_link);
+	spin_unlock(&xe->eudebug.lock);
+
+	eu_dbg(d, "session %lld detached with %d", d->session, err);
+
+	/* Our ref with the connection_link */
+	xe_eudebug_put(d);
+
+	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 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 (xe_eudebug_error(d))
+			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 struct xe_eudebug *
+xe_eudebug_for_task_get(struct xe_device *xe,
+			struct task_struct *task)
+{
+	struct xe_eudebug *d, *iter;
+
+	d = NULL;
+
+	rcu_read_lock();
+	for_each_debugger_rcu(iter, &xe->eudebug.list) {
+		if (!same_thread_group(iter->target_task, task))
+			continue;
+
+		if (kref_get_unless_zero(&iter->ref))
+			d = iter;
+
+		break;
+	}
+	rcu_read_unlock();
+
+	return d;
+}
+
+static struct task_struct *find_task_get(struct xe_file *xef)
+{
+	struct task_struct *task;
+	struct pid *pid;
+
+	rcu_read_lock();
+	pid = rcu_dereference(xef->drm->pid);
+	task = pid_task(pid, PIDTYPE_PID);
+	if (task)
+		get_task_struct(task);
+	rcu_read_unlock();
+
+	return task;
+}
+
+static struct xe_eudebug *
+xe_eudebug_get(struct xe_file *xef)
+{
+	struct task_struct *task;
+	struct xe_eudebug *d;
+
+	d = NULL;
+	task = find_task_get(xef);
+	if (task) {
+		d = xe_eudebug_for_task_get(to_xe_device(xef->drm->minor->dev),
+					    task);
+		put_task_struct(task);
+	}
+
+	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 xe_eudebug_event *event)
+{
+	const u64 wait_jiffies = msecs_to_jiffies(1000);
+	u64 last_read_detected_ts, last_head_seqno, start_ts;
+
+	xe_eudebug_assert(d, event->len > sizeof(struct 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 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) {
+			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(sizeof(*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 find_handle(struct xe_eudebug_resources *res,
+		       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(res, type);
+
+	mutex_lock(&res->lock);
+	h = __find_handle(r, key);
+	id = h ? h->id : -ENOENT;
+	mutex_unlock(&res->lock);
+
+	return id;
+}
+
+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->res, type);
+
+	mutex_lock(&d->res->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->res->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);
+	if (ret == -EEXIST || ret == -ENOTCONN) {
+		eu_dbg(d, "%d on adding %d", ret, type);
+		return 0;
+	}
+
+	if (ret < 0)
+		xe_eudebug_disconnect(d, 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->res, type);
+
+	mutex_lock(&d->res->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->res->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);
+	if (ret == -ENOENT || ret == -ENOTCONN) {
+		eu_dbg(d, "%d on removing %d", ret, type);
+		return 0;
+	}
+
+	if (ret < 0)
+		xe_eudebug_disconnect(d, ret);
+
+	return ret;
+}
+
+static struct xe_eudebug_event *
+__xe_eudebug_create_event(struct xe_eudebug *d,
+			  u64 seqno, u16 type, u16 flags, u32 len, gfp_t gfp)
+{
+	struct xe_eudebug_event *event;
+
+	xe_eudebug_assert(d, len > sizeof(*event));
+
+	event = kzalloc(len, gfp);
+	if (!event)
+		return NULL;
+
+	event->type = type;
+	event->flags = flags;
+	event->len = len;
+	event->seqno = seqno;
+
+	return event;
+}
+
+static struct xe_eudebug_event *
+xe_eudebug_create_event(struct xe_eudebug *d, u16 type, u64 seqno, u16 flags,
+			u32 len, gfp_t gfp)
+{
+	return __xe_eudebug_create_event(d, seqno, type, flags, len, gfp);
+}
+
+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 xe_eudebug_event *event;
+	const unsigned int max_event = DRM_XE_EUDEBUG_EVENT_VM;
+	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))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, user_event.type > max_event))
+		return -EINVAL;
+
+	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.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;
+	}
+
+	ret = 0;
+	spin_lock(&d->events.lock);
+	event = event_fifo_pending(d);
+	if (event) {
+		if (user_event.len < event->len) {
+			ret = -EMSGSIZE;
+		} else if (!kfifo_out(&d->events.fifo, &event, 1)) {
+			eu_warn(d, "internal fifo corruption");
+			ret = -ENOTCONN;
+		}
+	}
+	spin_unlock(&d->events.lock);
+
+	wake_up_all(&d->events.read_done);
+
+	if (ret == -EMSGSIZE && put_user(event->len, &user_orig->len))
+		ret = -EFAULT;
+
+	if (XE_IOCTL_DBG(xe, ret))
+		return ret;
+
+	if (!event) {
+		if (xe_eudebug_detached(d))
+			return -ENOTCONN;
+		if (!wait)
+			return -EAGAIN;
+
+		return -ENOENT;
+	}
+
+	if (copy_to_user(user_orig, event, event->len))
+		ret = -EFAULT;
+	else
+		eu_dbg(d, "event read: type=%u, flags=0x%x, seqno=%llu", event->type,
+		       event->flags, event->seqno);
+
+	kfree(event);
+
+	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,
+	.llseek		= no_llseek,
+	.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_xe_eudebug_connect *param)
+{
+	const u64 known_open_flags = 0;
+	unsigned long f_flags = 0;
+	struct xe_eudebug *d;
+	int fd, err;
+
+	if (param->extensions)
+		return -EINVAL;
+
+	if (!param->pid)
+		return -EINVAL;
+
+	if (param->flags & ~known_open_flags)
+		return -EINVAL;
+
+	if (param->version && param->version != DRM_XE_EUDEBUG_VERSION)
+		return -EINVAL;
+
+	param->version = DRM_XE_EUDEBUG_VERSION;
+
+	if (!xe->eudebug.available)
+		return -EOPNOTSUPP;
+
+	d = kzalloc(sizeof(*d), GFP_KERNEL);
+	if (!d)
+		return -ENOMEM;
+
+	kref_init(&d->ref);
+	spin_lock_init(&d->connection.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);
+
+	d->res = xe_eudebug_resources_alloc();
+	if (IS_ERR(d->res)) {
+		err = PTR_ERR(d->res);
+		goto err_free;
+	}
+
+	err = xe_eudebug_attach(xe, d, param->pid);
+	if (err)
+		goto err_free_res;
+
+	fd = anon_inode_getfd("[xe_eudebug]", &fops, d, f_flags);
+	if (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_destroy_resources(d);
+err_free:
+	kfree(d);
+
+	return 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;
+	int ret = 0;
+
+	ret = xe_eudebug_connect(xe, param);
+
+	return ret;
+}
+
+void xe_eudebug_init(struct xe_device *xe)
+{
+	spin_lock_init(&xe->eudebug.lock);
+	INIT_LIST_HEAD(&xe->eudebug.list);
+	INIT_LIST_HEAD(&xe->clients.list);
+
+	xe->eudebug.available = true;
+}
+
+void xe_eudebug_fini(struct xe_device *xe)
+{
+	xe_assert(xe, list_empty_careful(&xe->eudebug.list));
+}
+
+static int send_open_event(struct xe_eudebug *d, u32 flags, const u64 handle,
+			   const u64 seqno)
+{
+	struct xe_eudebug_event *event;
+	struct xe_eudebug_event_open *eo;
+
+	if (!handle)
+		return -EINVAL;
+
+	if (XE_WARN_ON((long)handle >= INT_MAX))
+		return -EINVAL;
+
+	event = xe_eudebug_create_event(d, DRM_XE_EUDEBUG_EVENT_OPEN, seqno,
+					flags, sizeof(*eo), GFP_KERNEL);
+	if (!event)
+		return -ENOMEM;
+
+	eo = cast_event(eo, event);
+
+	write_member(struct drm_xe_eudebug_event_client, eo,
+		     client_handle, handle);
+
+	return xe_eudebug_queue_event(d, event);
+}
+
+static int client_create_event(struct xe_eudebug *d, struct xe_file *xef)
+{
+	u64 seqno;
+	int ret;
+
+	ret = xe_eudebug_add_handle(d, XE_EUDEBUG_RES_TYPE_CLIENT, xef, &seqno);
+	if (ret > 0)
+		ret = send_open_event(d, DRM_XE_EUDEBUG_EVENT_CREATE,
+				      ret, seqno);
+
+	return ret;
+}
+
+static int client_destroy_event(struct xe_eudebug *d, struct xe_file *xef)
+{
+	u64 seqno;
+	int ret;
+
+	ret = xe_eudebug_remove_handle(d, XE_EUDEBUG_RES_TYPE_CLIENT,
+				       xef, &seqno);
+	if (ret > 0)
+		ret = send_open_event(d, DRM_XE_EUDEBUG_EVENT_DESTROY,
+				      ret, seqno);
+
+	return ret;
+}
+
+#define xe_eudebug_event_put(_d, _err) ({ \
+	if ((_err)) \
+		xe_eudebug_disconnect((_d), (_err)); \
+	xe_eudebug_put((_d)); \
+	})
+
+void xe_eudebug_file_open(struct xe_file *xef)
+{
+	struct xe_eudebug *d;
+
+	INIT_LIST_HEAD(&xef->eudebug.client_link);
+	spin_lock(&xef->xe->clients.lock);
+	list_add_tail(&xef->eudebug.client_link, &xef->xe->clients.list);
+	spin_unlock(&xef->xe->clients.lock);
+
+	d = xe_eudebug_get(xef);
+	if (!d)
+		return;
+
+	xe_eudebug_event_put(d, client_create_event(d, xef));
+}
+
+void xe_eudebug_file_close(struct xe_file *xef)
+{
+	struct xe_eudebug *d;
+
+	d = xe_eudebug_get(xef);
+	if (d)
+		xe_eudebug_event_put(d, client_destroy_event(d, xef));
+
+	spin_lock(&xef->xe->clients.lock);
+	list_del_init(&xef->eudebug.client_link);
+	spin_unlock(&xef->xe->clients.lock);
+}
+
+static int send_vm_event(struct xe_eudebug *d, u32 flags,
+			 const u64 client_handle,
+			 const u64 vm_handle,
+			 const u64 seqno)
+{
+	struct xe_eudebug_event *event;
+	struct xe_eudebug_event_vm *e;
+
+	event = xe_eudebug_create_event(d, DRM_XE_EUDEBUG_EVENT_VM,
+					seqno, flags, sizeof(*e), GFP_KERNEL);
+	if (!event)
+		return -ENOMEM;
+
+	e = cast_event(e, event);
+
+	write_member(struct drm_xe_eudebug_event_vm, e, client_handle, client_handle);
+	write_member(struct drm_xe_eudebug_event_vm, 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 h_c, h_vm;
+	u64 seqno;
+	int ret;
+
+	if (!xe_vm_in_lr_mode(vm))
+		return 0;
+
+	h_c = find_handle(d->res, XE_EUDEBUG_RES_TYPE_CLIENT, xef);
+	if (h_c < 0)
+		return h_c;
+
+	xe_eudebug_assert(d, h_c);
+
+	h_vm = xe_eudebug_add_handle(d, XE_EUDEBUG_RES_TYPE_VM, vm, &seqno);
+	if (h_vm <= 0)
+		return h_vm;
+
+	ret = send_vm_event(d, DRM_XE_EUDEBUG_EVENT_CREATE, h_c, h_vm, seqno);
+
+	return ret;
+}
+
+static int vm_destroy_event(struct xe_eudebug *d,
+			    struct xe_file *xef, struct xe_vm *vm)
+{
+	int h_c, h_vm;
+	u64 seqno;
+
+	if (!xe_vm_in_lr_mode(vm))
+		return 0;
+
+	h_c = find_handle(d->res, XE_EUDEBUG_RES_TYPE_CLIENT, xef);
+	if (h_c < 0) {
+		XE_WARN_ON("no client found for vm");
+		eu_warn(d, "no client found for vm");
+		return h_c;
+	}
+
+	xe_eudebug_assert(d, h_c);
+
+	h_vm = xe_eudebug_remove_handle(d, XE_EUDEBUG_RES_TYPE_VM, vm, &seqno);
+	if (h_vm <= 0)
+		return h_vm;
+
+	return send_vm_event(d, DRM_XE_EUDEBUG_EVENT_DESTROY, h_c, h_vm, seqno);
+}
+
+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));
+}
diff --git a/drivers/gpu/drm/xe/xe_eudebug.h b/drivers/gpu/drm/xe/xe_eudebug.h
new file mode 100644
index 000000000000..e3247365f72f
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_eudebug.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#ifndef _XE_EUDEBUG_H_
+
+struct drm_device;
+struct drm_file;
+struct xe_device;
+struct xe_file;
+struct xe_vm;
+
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+
+int xe_eudebug_connect_ioctl(struct drm_device *dev,
+			     void *data,
+			     struct drm_file *file);
+
+void xe_eudebug_init(struct xe_device *xe);
+void xe_eudebug_fini(struct xe_device *xe);
+
+void xe_eudebug_file_open(struct xe_file *xef);
+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);
+
+#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 void xe_eudebug_fini(struct xe_device *xe) { }
+
+static inline void xe_eudebug_file_open(struct xe_file *xef) { }
+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
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..a5185f18f640
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#ifndef __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>
+
+#include <uapi/drm/xe_drm.h>
+
+struct xe_device;
+struct task_struct;
+struct xe_eudebug_event;
+
+#define CONFIG_DRM_XE_DEBUGGER_EVENT_QUEUE_SIZE 64
+
+/**
+ * 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_CLIENT	0
+#define XE_EUDEBUG_RES_TYPE_VM		1
+#define XE_EUDEBUG_RES_TYPE_COUNT	(XE_EUDEBUG_RES_TYPE_VM + 1)
+
+/**
+ * struct xe_eudebug_resources - eudebug resources for all types
+ */
+struct xe_eudebug_resources {
+	/** @lock: guards access into rt */
+	struct mutex lock;
+
+	/** @rt: resource maps for all types */
+	struct xe_eudebug_resource rt[XE_EUDEBUG_RES_TYPE_COUNT];
+};
+
+/**
+ * struct xe_eudebug - Top level struct for eudebug: the connection
+ */
+struct xe_eudebug {
+	/** @ref: kref counter for this struct */
+	struct kref ref;
+
+	/** @rcu: rcu_head for rcu destruction */
+	struct rcu_head rcu;
+
+	/** @connection_link: our link into the xe_device:eudebug.list */
+	struct list_head connection_link;
+
+	struct {
+		/** @status: connected = 1, disconnected = error */
+#define XE_EUDEBUG_STATUS_CONNECTED 1
+		int status;
+
+		/** @lock: guards access to status */
+		spinlock_t lock;
+	} connection;
+
+	/** @xe: the parent device we are serving */
+	struct xe_device *xe;
+
+	/** @target_task: the task that we are debugging */
+	struct task_struct *target_task;
+
+	/** @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 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;
+
+};
+
+/**
+ * struct xe_eudebug_event - Internal base event struct for eudebug
+ */
+struct xe_eudebug_event {
+	/** @len: length of this event, including payload */
+	u32 len;
+
+	/** @type: message type */
+	u16 type;
+
+	/** @flags: message flags */
+	u16 flags;
+
+	/** @seqno: sequence number for ordering */
+	u64 seqno;
+
+	/** @reserved: reserved field MBZ */
+	u64 reserved;
+
+	/** @data: payload bytes */
+	u8 data[];
+};
+
+/**
+ * struct xe_eudebug_event_open - Internal event for client open/close
+ */
+struct xe_eudebug_event_open {
+	/** @base: base event */
+	struct xe_eudebug_event base;
+
+	/** @client_handle: opaque handle for client */
+	u64 client_handle;
+};
+
+/**
+ * struct xe_eudebug_event_vm - Internal event for vm open/close
+ */
+struct xe_eudebug_event_vm {
+	/** @base: base event */
+	struct xe_eudebug_event base;
+
+	/** @client_handle: client containing the vm open/close */
+	u64 client_handle;
+
+	/** @vm_handle: vm handle it's open/close */
+	u64 vm_handle;
+};
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index ce9dca4d4e87..9701a7efe257 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_gt_pagefault.h"
 #include "xe_gt_tlb_invalidation.h"
@@ -1796,6 +1797,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:
@@ -1827,8 +1830,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 b6fbe4988f2e..7e66e9117f49 100644
--- a/include/uapi/drm/xe_drm.h
+++ b/include/uapi/drm/xe_drm.h
@@ -102,6 +102,7 @@ extern "C" {
 #define DRM_XE_EXEC			0x09
 #define DRM_XE_WAIT_USER_FENCE		0x0a
 #define DRM_XE_OBSERVATION		0x0b
+#define DRM_XE_EUDEBUG_CONNECT		0x0c
 
 /* Must be kept compact -- no holes */
 
@@ -117,6 +118,7 @@ extern "C" {
 #define DRM_IOCTL_XE_EXEC			DRM_IOW(DRM_COMMAND_BASE + DRM_XE_EXEC, struct drm_xe_exec)
 #define DRM_IOCTL_XE_WAIT_USER_FENCE		DRM_IOWR(DRM_COMMAND_BASE + DRM_XE_WAIT_USER_FENCE, struct drm_xe_wait_user_fence)
 #define DRM_IOCTL_XE_OBSERVATION		DRM_IOW(DRM_COMMAND_BASE + DRM_XE_OBSERVATION, struct drm_xe_observation_param)
+#define DRM_IOCTL_XE_EUDEBUG_CONNECT		DRM_IOWR(DRM_COMMAND_BASE + DRM_XE_EUDEBUG_CONNECT, struct drm_xe_eudebug_connect)
 
 /**
  * DOC: Xe IOCTL Extensions
@@ -1694,6 +1696,25 @@ struct drm_xe_oa_stream_info {
 	__u64 reserved[3];
 };
 
+/*
+ * Debugger ABI (ioctl and events) Version History:
+ * 0 - No debugger available
+ * 1 - Initial version
+ */
+#define DRM_XE_EUDEBUG_VERSION 1
+
+struct drm_xe_eudebug_connect {
+	/** @extensions: Pointer to the first extension struct, if any */
+	__u64 extensions;
+
+	__u64 pid; /* input: Target process ID */
+	__u32 flags; /* MBZ */
+
+	__u32 version; /* output: current ABI (ioctl / events) version */
+};
+
+#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..acf6071c82bf
--- /dev/null
+++ b/include/uapi/drm/xe_drm_eudebug.h
@@ -0,0 +1,56 @@
+/* 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
+
+/**
+ * Do a eudebug event read for a debugger connection.
+ *
+ * This ioctl is available in debug version 1.
+ */
+#define DRM_XE_EUDEBUG_IOCTL_READ_EVENT _IO('j', 0x0)
+
+/* XXX: Document events to match their internal counterparts when moved to xe_drm.h */
+struct drm_xe_eudebug_event {
+	__u32 len;
+
+	__u16 type;
+#define DRM_XE_EUDEBUG_EVENT_NONE		0
+#define DRM_XE_EUDEBUG_EVENT_READ		1
+#define DRM_XE_EUDEBUG_EVENT_OPEN		2
+#define DRM_XE_EUDEBUG_EVENT_VM			3
+
+	__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)
+	__u64 seqno;
+	__u64 reserved;
+};
+
+struct drm_xe_eudebug_event_client {
+	struct drm_xe_eudebug_event base;
+
+	__u64 client_handle; /* This is unique per debug connection */
+};
+
+struct drm_xe_eudebug_event_vm {
+	struct drm_xe_eudebug_event base;
+
+	__u64 client_handle;
+	__u64 vm_handle;
+};
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
-- 
2.34.1


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

* [PATCH 03/18] drm/xe/eudebug: Introduce discovery for resources
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
  2024-10-01 14:42 ` [PATCH 01/18] ptrace: export ptrace_may_access Mika Kuoppala
  2024-10-01 14:42 ` [PATCH 02/18] drm/xe/eudebug: Introduce eudebug support Mika Kuoppala
@ 2024-10-01 14:42 ` Mika Kuoppala
  2024-10-03  0:41   ` Matthew Brost
  2024-10-01 14:42 ` [PATCH 04/18] drm/xe/eudebug: Introduce exec_queue events Mika Kuoppala
                   ` (17 subsequent siblings)
  20 siblings, 1 reply; 40+ messages in thread
From: Mika Kuoppala @ 2024-10-01 14:42 UTC (permalink / raw)
  To: intel-xe; +Cc: Mika Kuoppala, Matthew Brost, Dominik Grzegorzek,
	Maciej Patelczyk

Debugger connection can happen way after the client has
created and destroyed arbitrary number of resources.

We need to playback all currently existing resources for the
debugger. The client is held until this so called discovery
process, executed by workqueue, is complete.

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

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

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>
---
 drivers/gpu/drm/xe/xe_device.c        |  10 +-
 drivers/gpu/drm/xe/xe_device.h        |  34 +++++++
 drivers/gpu/drm/xe/xe_device_types.h  |   6 ++
 drivers/gpu/drm/xe/xe_eudebug.c       | 135 +++++++++++++++++++++++++-
 drivers/gpu/drm/xe/xe_eudebug_types.h |   7 ++
 5 files changed, 185 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index 5615e2c23bf6..ec5eedbbf320 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -215,8 +215,11 @@ static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 		return -ECANCELED;
 
 	ret = xe_pm_runtime_get_ioctl(xe);
-	if (ret >= 0)
+	if (ret >= 0) {
+		xe_eudebug_discovery_lock(xe, cmd);
 		ret = drm_ioctl(file, cmd, arg);
+		xe_eudebug_discovery_unlock(xe, cmd);
+	}
 	xe_pm_runtime_put(xe);
 
 	return ret;
@@ -233,8 +236,11 @@ static long xe_drm_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo
 		return -ECANCELED;
 
 	ret = xe_pm_runtime_get_ioctl(xe);
-	if (ret >= 0)
+	if (ret >= 0) {
+		xe_eudebug_discovery_lock(xe, cmd);
 		ret = drm_compat_ioctl(file, cmd, arg);
+		xe_eudebug_discovery_unlock(xe, cmd);
+	}
 	xe_pm_runtime_put(xe);
 
 	return ret;
diff --git a/drivers/gpu/drm/xe/xe_device.h b/drivers/gpu/drm/xe/xe_device.h
index 4c3f0ebe78a9..b2fc85b1d26e 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"
@@ -191,4 +192,37 @@ void xe_device_declare_wedged(struct xe_device *xe);
 struct xe_file *xe_file_get(struct xe_file *xef);
 void xe_file_put(struct xe_file *xef);
 
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+static inline int xe_eudebug_needs_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:
+	case DRM_XE_EUDEBUG_CONNECT:
+		return 1;
+	}
+
+	return 0;
+}
+
+static inline void xe_eudebug_discovery_lock(struct xe_device *xe, unsigned int cmd)
+{
+	if (xe_eudebug_needs_lock(cmd))
+		down_read(&xe->eudebug.discovery_lock);
+}
+static inline void xe_eudebug_discovery_unlock(struct xe_device *xe, unsigned int cmd)
+{
+	if (xe_eudebug_needs_lock(cmd))
+		up_read(&xe->eudebug.discovery_lock);
+}
+#else
+static inline void xe_eudebug_discovery_lock(struct xe_device *xe, unsigned int cmd) { }
+static inline void xe_eudebug_discovery_unlock(struct xe_device *xe, 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 cb4b52888a4b..54ceeee7cf75 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -542,6 +542,12 @@ struct xe_device {
 
 		/** @available: is the debugging functionality available */
 		bool available;
+
+		/** @ordered_wq: used to discovery */
+		struct workqueue_struct *ordered_wq;
+
+		/** discovery_lock: used for discovery to block xe ioctls */
+		struct rw_semaphore discovery_lock;
 	} eudebug;
 #endif
 
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index ea0cfd7697aa..c294e2c6152b 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -299,6 +299,8 @@ static bool xe_eudebug_detach(struct xe_device *xe,
 	}
 	spin_unlock(&d->connection.lock);
 
+	flush_work(&d->discovery_work);
+
 	if (!detached)
 		return false;
 
@@ -409,7 +411,7 @@ static struct task_struct *find_task_get(struct xe_file *xef)
 }
 
 static struct xe_eudebug *
-xe_eudebug_get(struct xe_file *xef)
+_xe_eudebug_get(struct xe_file *xef)
 {
 	struct task_struct *task;
 	struct xe_eudebug *d;
@@ -433,6 +435,24 @@ xe_eudebug_get(struct xe_file *xef)
 	return d;
 }
 
+static struct xe_eudebug *
+xe_eudebug_get(struct xe_file *xef)
+{
+	struct xe_eudebug *d;
+
+	lockdep_assert_held(&xef->xe->eudebug.discovery_lock);
+
+	d = _xe_eudebug_get(xef);
+	if (d) {
+		if (!completion_done(&d->discovery)) {
+			xe_eudebug_put(d);
+			d = NULL;
+		}
+	}
+
+	return d;
+}
+
 static int xe_eudebug_queue_event(struct xe_eudebug *d,
 				  struct xe_eudebug_event *event)
 {
@@ -810,6 +830,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,
@@ -832,6 +856,8 @@ static const struct file_operations fops = {
 	.unlocked_ioctl	= xe_eudebug_ioctl,
 };
 
+static void discovery_work_fn(struct work_struct *work);
+
 static int
 xe_eudebug_connect(struct xe_device *xe,
 		   struct drm_xe_eudebug_connect *param)
@@ -866,9 +892,11 @@ xe_eudebug_connect(struct xe_device *xe,
 	spin_lock_init(&d->connection.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);
 
 	d->res = xe_eudebug_resources_alloc();
 	if (IS_ERR(d->res)) {
@@ -886,6 +914,9 @@ xe_eudebug_connect(struct xe_device *xe,
 		goto err_detach;
 	}
 
+	kref_get(&d->ref);
+	queue_work(xe->eudebug.ordered_wq, &d->discovery_work);
+
 	eu_dbg(d, "connected session %lld", d->session);
 
 	return fd;
@@ -918,13 +949,18 @@ void xe_eudebug_init(struct xe_device *xe)
 	spin_lock_init(&xe->eudebug.lock);
 	INIT_LIST_HEAD(&xe->eudebug.list);
 	INIT_LIST_HEAD(&xe->clients.list);
+	init_rwsem(&xe->eudebug.discovery_lock);
 
-	xe->eudebug.available = true;
+	xe->eudebug.ordered_wq = alloc_ordered_workqueue("xe-eudebug-ordered-wq", 0);
+	xe->eudebug.available = !!xe->eudebug.ordered_wq;
 }
 
 void xe_eudebug_fini(struct xe_device *xe)
 {
 	xe_assert(xe, list_empty_careful(&xe->eudebug.list));
+
+	if (xe->eudebug.ordered_wq)
+		destroy_workqueue(xe->eudebug.ordered_wq);
 }
 
 static int send_open_event(struct xe_eudebug *d, u32 flags, const u64 handle,
@@ -990,21 +1026,25 @@ void xe_eudebug_file_open(struct xe_file *xef)
 	struct xe_eudebug *d;
 
 	INIT_LIST_HEAD(&xef->eudebug.client_link);
+
+	down_read(&xef->xe->eudebug.discovery_lock);
+
 	spin_lock(&xef->xe->clients.lock);
 	list_add_tail(&xef->eudebug.client_link, &xef->xe->clients.list);
 	spin_unlock(&xef->xe->clients.lock);
 
 	d = xe_eudebug_get(xef);
-	if (!d)
-		return;
+	if (d)
+		xe_eudebug_event_put(d, client_create_event(d, xef));
 
-	xe_eudebug_event_put(d, client_create_event(d, xef));
+	up_read(&xef->xe->eudebug.discovery_lock);
 }
 
 void xe_eudebug_file_close(struct xe_file *xef)
 {
 	struct xe_eudebug *d;
 
+	down_read(&xef->xe->eudebug.discovery_lock);
 	d = xe_eudebug_get(xef);
 	if (d)
 		xe_eudebug_event_put(d, client_destroy_event(d, xef));
@@ -1012,6 +1052,8 @@ void xe_eudebug_file_close(struct xe_file *xef)
 	spin_lock(&xef->xe->clients.lock);
 	list_del_init(&xef->eudebug.client_link);
 	spin_unlock(&xef->xe->clients.lock);
+
+	up_read(&xef->xe->eudebug.discovery_lock);
 }
 
 static int send_vm_event(struct xe_eudebug *d, u32 flags,
@@ -1112,3 +1154,86 @@ 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 int discover_client(struct xe_eudebug *d, struct xe_file *xef)
+{
+	struct xe_vm *vm;
+	unsigned long i;
+	int err;
+
+	err = client_create_event(d, xef);
+	if (err)
+		return err;
+
+	xa_for_each(&xef->vm.xa, i, vm) {
+		err = vm_create_event(d, xef, vm);
+		if (err)
+			break;
+	}
+
+	return err;
+}
+
+static bool xe_eudebug_task_match(struct xe_eudebug *d, struct xe_file *xef)
+{
+	struct task_struct *task;
+	bool match;
+
+	task = find_task_get(xef);
+	if (!task)
+		return false;
+
+	match = same_thread_group(d->target_task, task);
+
+	put_task_struct(task);
+
+	return match;
+}
+
+static void discover_clients(struct xe_device *xe, struct xe_eudebug *d)
+{
+	struct xe_file *xef;
+	int err;
+
+	list_for_each_entry(xef, &xe->clients.list, eudebug.client_link) {
+		if (xe_eudebug_detached(d))
+			break;
+
+		if (xe_eudebug_task_match(d, xef))
+			err = discover_client(d, xef);
+		else
+			err = 0;
+
+		if (err) {
+			eu_dbg(d, "discover client %p: %d\n", xef, err);
+			break;
+		}
+	}
+}
+
+static void discovery_work_fn(struct work_struct *work)
+{
+	struct xe_eudebug *d = container_of(work, typeof(*d),
+					    discovery_work);
+	struct xe_device *xe = d->xe;
+
+	if (xe_eudebug_detached(d)) {
+		complete_all(&d->discovery);
+		xe_eudebug_put(d);
+		return;
+	}
+
+	down_write(&xe->eudebug.discovery_lock);
+
+	eu_dbg(d, "Discovery start for %lld\n", d->session);
+
+	discover_clients(xe, d);
+
+	eu_dbg(d, "Discovery end for %lld\n", d->session);
+
+	complete_all(&d->discovery);
+
+	up_write(&xe->eudebug.discovery_lock);
+
+	xe_eudebug_put(d);
+}
diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
index a5185f18f640..080a821db3e4 100644
--- a/drivers/gpu/drm/xe/xe_eudebug_types.h
+++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
@@ -19,6 +19,7 @@
 struct xe_device;
 struct task_struct;
 struct xe_eudebug_event;
+struct workqueue_struct;
 
 #define CONFIG_DRM_XE_DEBUGGER_EVENT_QUEUE_SIZE 64
 
@@ -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.34.1


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

* [PATCH 04/18] drm/xe/eudebug: Introduce exec_queue events
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
                   ` (2 preceding siblings ...)
  2024-10-01 14:42 ` [PATCH 03/18] drm/xe/eudebug: Introduce discovery for resources Mika Kuoppala
@ 2024-10-01 14:42 ` Mika Kuoppala
  2024-10-01 14:42 ` [PATCH 05/18] drm/xe/eudebug: hw enablement for eudebug Mika Kuoppala
                   ` (16 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Mika Kuoppala @ 2024-10-01 14:42 UTC (permalink / raw)
  To: intel-xe; +Cc: Dominik Grzegorzek, Maciej Patelczyk, Mika Kuoppala

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

Inform debugger about creation and destruction of exec_queues.

1) Use user engine class types instead of internal xe_engine_class enum
   in exec_queue event.

2) During discovery do not advertise every execqueue created, only ones
   with class render or compute.

v2: - Only track long running queues
    - Checkpatch (Tilak)

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>
---
 drivers/gpu/drm/xe/xe_eudebug.c       | 187 +++++++++++++++++++++++++-
 drivers/gpu/drm/xe/xe_eudebug.h       |   7 +
 drivers/gpu/drm/xe/xe_eudebug_types.h |  31 ++++-
 drivers/gpu/drm/xe/xe_exec_queue.c    |   5 +
 include/uapi/drm/xe_drm_eudebug.h     |  12 ++
 5 files changed, 240 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index c294e2c6152b..4706320b39b6 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -14,6 +14,7 @@
 #include "xe_device.h"
 #include "xe_eudebug.h"
 #include "xe_eudebug_types.h"
+#include "xe_exec_queue.h"
 #include "xe_macros.h"
 #include "xe_vm.h"
 
@@ -748,7 +749,7 @@ static long xe_eudebug_read_event(struct xe_eudebug *d,
 		u64_to_user_ptr(arg);
 	struct drm_xe_eudebug_event user_event;
 	struct xe_eudebug_event *event;
-	const unsigned int max_event = DRM_XE_EUDEBUG_EVENT_VM;
+	const unsigned int max_event = DRM_XE_EUDEBUG_EVENT_EXEC_QUEUE;
 	long ret = 0;
 
 	if (XE_IOCTL_DBG(xe, copy_from_user(&user_event, user_orig, sizeof(user_event))))
@@ -1155,8 +1156,183 @@ 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 bool exec_queue_class_is_tracked(enum xe_engine_class class)
+{
+	return class == XE_ENGINE_CLASS_COMPUTE ||
+		class == XE_ENGINE_CLASS_RENDER;
+}
+
+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 client_handle, u64 vm_handle,
+				 u64 exec_queue_handle, enum xe_engine_class class,
+				 u32 width, u64 *lrc_handles, u64 seqno)
+{
+	struct xe_eudebug_event *event;
+	struct 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 (!exec_queue_class_is_tracked(class))
+		return -EINVAL;
+
+	event = xe_eudebug_create_event(d, DRM_XE_EUDEBUG_EVENT_EXEC_QUEUE,
+					seqno, flags, sz, GFP_KERNEL);
+	if (!event)
+		return -ENOMEM;
+
+	e = cast_event(e, event);
+
+	write_member(struct drm_xe_eudebug_event_exec_queue, e, client_handle, client_handle);
+	write_member(struct drm_xe_eudebug_event_exec_queue, e, vm_handle, vm_handle);
+	write_member(struct drm_xe_eudebug_event_exec_queue, e, exec_queue_handle,
+		     exec_queue_handle);
+	write_member(struct drm_xe_eudebug_event_exec_queue, e, engine_class, xe_engine_class);
+	write_member(struct drm_xe_eudebug_event_exec_queue, 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_c, h_vm, h_queue;
+	u64 h_lrc[XE_HW_ENGINE_MAX_INSTANCE], seqno;
+	int i;
+
+	if (!xe_exec_queue_is_lr(q))
+		return 0;
+
+	h_c = find_handle(d->res, XE_EUDEBUG_RES_TYPE_CLIENT, xef);
+	if (h_c < 0)
+		return h_c;
+
+	h_vm = find_handle(d->res, 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
+	 */
+
+	return send_exec_queue_event(d, DRM_XE_EUDEBUG_EVENT_CREATE,
+				     h_c, h_vm, h_queue, q->class,
+				     q->width, h_lrc, seqno);
+}
+
+static int exec_queue_destroy_event(struct xe_eudebug *d,
+				    struct xe_file *xef,
+				    struct xe_exec_queue *q)
+{
+	int h_c, h_vm, h_queue;
+	u64 h_lrc[XE_HW_ENGINE_MAX_INSTANCE], seqno;
+	int i;
+
+	if (!xe_exec_queue_is_lr(q))
+		return 0;
+
+	h_c = find_handle(d->res, XE_EUDEBUG_RES_TYPE_CLIENT, xef);
+	if (h_c < 0)
+		return h_c;
+
+	h_vm = find_handle(d->res, 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++) {
+		int ret;
+
+		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;
+	}
+
+	return send_exec_queue_event(d, DRM_XE_EUDEBUG_EVENT_DESTROY,
+				     h_c, h_vm, h_queue, q->class,
+				     q->width, h_lrc, seqno);
+}
+
+void xe_eudebug_exec_queue_create(struct xe_file *xef, struct xe_exec_queue *q)
+{
+	struct xe_eudebug *d;
+
+	if (!exec_queue_class_is_tracked(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 (!exec_queue_class_is_tracked(q->class))
+		return;
+
+	d = xe_eudebug_get(xef);
+	if (!d)
+		return;
+
+	xe_eudebug_event_put(d, exec_queue_destroy_event(d, xef, q));
+}
+
 static int discover_client(struct xe_eudebug *d, struct xe_file *xef)
 {
+	struct xe_exec_queue *q;
 	struct xe_vm *vm;
 	unsigned long i;
 	int err;
@@ -1171,6 +1347,15 @@ static int discover_client(struct xe_eudebug *d, struct xe_file *xef)
 			break;
 	}
 
+	xa_for_each(&xef->exec_queue.xa, i, q) {
+		if (!exec_queue_class_is_tracked(q->class))
+			continue;
+
+		err = exec_queue_create_event(d, xef, q);
+		if (err)
+			break;
+	}
+
 	return err;
 }
 
diff --git a/drivers/gpu/drm/xe/xe_eudebug.h b/drivers/gpu/drm/xe/xe_eudebug.h
index e3247365f72f..326ddbd50651 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.h
+++ b/drivers/gpu/drm/xe/xe_eudebug.h
@@ -10,6 +10,7 @@ struct drm_file;
 struct xe_device;
 struct xe_file;
 struct xe_vm;
+struct xe_exec_queue;
 
 #if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
 
@@ -26,6 +27,9 @@ 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);
 
+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,
@@ -41,6 +45,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
diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
index 080a821db3e4..6428905a0557 100644
--- a/drivers/gpu/drm/xe/xe_eudebug_types.h
+++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
@@ -50,7 +50,9 @@ struct xe_eudebug_resource {
 
 #define XE_EUDEBUG_RES_TYPE_CLIENT	0
 #define XE_EUDEBUG_RES_TYPE_VM		1
-#define XE_EUDEBUG_RES_TYPE_COUNT	(XE_EUDEBUG_RES_TYPE_VM + 1)
+#define XE_EUDEBUG_RES_TYPE_EXEC_QUEUE	2
+#define XE_EUDEBUG_RES_TYPE_LRC		3
+#define XE_EUDEBUG_RES_TYPE_COUNT	(XE_EUDEBUG_RES_TYPE_LRC + 1)
 
 /**
  * struct xe_eudebug_resources - eudebug resources for all types
@@ -173,4 +175,31 @@ struct xe_eudebug_event_vm {
 	u64 vm_handle;
 };
 
+/**
+ * struct xe_eudebug_event_exec_queue - Internal event for
+ * exec_queue create/destroy
+ */
+struct xe_eudebug_event_exec_queue {
+	/** @base: base event */
+	struct xe_eudebug_event base;
+
+	/** @client_handle: client for the engine create/destroy */
+	u64 client_handle;
+
+	/** @vm_handle: vm handle for the engine create/destroy */
+	u64 vm_handle;
+
+	/** @exec_queue_handle: engine handle */
+	u64 exec_queue_handle;
+
+	/** @engine_handle: engine class */
+	u32 engine_class;
+
+	/** @width: submission width (number BB per exec) for this exec queue */
+	u32 width;
+
+	/** @lrc_handles: handles for each logical ring context created with this exec queue */
+	u64 lrc_handle[];
+};
+
 #endif
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
index d098d2dd1b2d..e6b2487de48e 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.c
+++ b/drivers/gpu/drm/xe/xe_exec_queue.c
@@ -23,6 +23,7 @@
 #include "xe_ring_ops_types.h"
 #include "xe_trace.h"
 #include "xe_vm.h"
+#include "xe_eudebug.h"
 
 enum xe_exec_queue_sched_prop {
 	XE_EXEC_QUEUE_JOB_TIMEOUT = 0,
@@ -644,6 +645,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:
@@ -827,6 +830,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/include/uapi/drm/xe_drm_eudebug.h b/include/uapi/drm/xe_drm_eudebug.h
index acf6071c82bf..ac44e890152a 100644
--- a/include/uapi/drm/xe_drm_eudebug.h
+++ b/include/uapi/drm/xe_drm_eudebug.h
@@ -26,6 +26,7 @@ struct drm_xe_eudebug_event {
 #define DRM_XE_EUDEBUG_EVENT_READ		1
 #define DRM_XE_EUDEBUG_EVENT_OPEN		2
 #define DRM_XE_EUDEBUG_EVENT_VM			3
+#define DRM_XE_EUDEBUG_EVENT_EXEC_QUEUE		4
 
 	__u16 flags;
 #define DRM_XE_EUDEBUG_EVENT_CREATE		(1 << 0)
@@ -49,6 +50,17 @@ struct drm_xe_eudebug_event_vm {
 	__u64 vm_handle;
 };
 
+struct drm_xe_eudebug_event_exec_queue {
+	struct drm_xe_eudebug_event base;
+
+	__u64 client_handle;
+	__u64 vm_handle;
+	__u64 exec_queue_handle;
+	__u32 engine_class;
+	__u32 width;
+	__u64 lrc_handle[];
+};
+
 #if defined(__cplusplus)
 }
 #endif
-- 
2.34.1


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

* [PATCH 05/18] drm/xe/eudebug: hw enablement for eudebug
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
                   ` (3 preceding siblings ...)
  2024-10-01 14:42 ` [PATCH 04/18] drm/xe/eudebug: Introduce exec_queue events Mika Kuoppala
@ 2024-10-01 14:42 ` Mika Kuoppala
  2024-10-01 14:42 ` [PATCH 06/18] drm/xe: Add EUDEBUG_ENABLE exec queue property Mika Kuoppala
                   ` (15 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Mika Kuoppala @ 2024-10-01 14:42 UTC (permalink / raw)
  To: intel-xe; +Cc: 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.

Signed-off-by: Dominik Grzegorzek <dominik.grzegorzek@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 drivers/gpu/drm/xe/regs/xe_engine_regs.h |  4 ++
 drivers/gpu/drm/xe/regs/xe_gt_regs.h     | 10 ++++
 drivers/gpu/drm/xe/xe_eudebug.c          | 70 ++++++++++++++++++++++++
 drivers/gpu/drm/xe/xe_eudebug.h          |  3 +
 drivers/gpu/drm/xe/xe_hw_engine.c        |  2 +
 drivers/gpu/drm/xe/xe_wa_oob.rules       |  2 +
 6 files changed, 91 insertions(+)

diff --git a/drivers/gpu/drm/xe/regs/xe_engine_regs.h b/drivers/gpu/drm/xe/regs/xe_engine_regs.h
index 81b71903675e..e004423a66bf 100644
--- a/drivers/gpu/drm/xe/regs/xe_engine_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_engine_regs.h
@@ -115,6 +115,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 fb80042cbe0d..2780541feb67 100644
--- a/drivers/gpu/drm/xe/regs/xe_gt_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
@@ -453,6 +453,14 @@
 #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)
@@ -479,11 +487,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)
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index 4706320b39b6..f90b15fc4964 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -10,13 +10,21 @@
 
 #include <drm/drm_managed.h>
 
+#include <generated/xe_wa_oob.h>
+
+#include "regs/xe_gt_regs.h"
+#include "regs/xe_engine_regs.h"
+
 #include "xe_assert.h"
 #include "xe_device.h"
 #include "xe_eudebug.h"
 #include "xe_eudebug_types.h"
 #include "xe_exec_queue.h"
 #include "xe_macros.h"
+#include "xe_reg_sr.h"
+#include "xe_rtp.h"
 #include "xe_vm.h"
+#include "xe_wa.h"
 
 /*
  * If there is no detected event read by userspace, during this period, assume
@@ -945,6 +953,68 @@ int xe_eudebug_connect_ioctl(struct drm_device *dev,
 	return ret;
 }
 
+#undef XE_REG_MCR
+#define XE_REG_MCR(...)     XE_REG(__VA_ARGS__, .mcr = 1)
+
+void xe_eudebug_init_hw_engine(struct xe_hw_engine *hwe)
+{
+	struct xe_gt *gt = hwe->gt;
+	struct xe_device *xe = gt_to_xe(gt);
+
+	if (!xe->eudebug.available)
+		return;
+
+	if (!xe_rtp_match_first_render_or_compute(gt, hwe))
+		return;
+
+	if (XE_WA(gt, 18022722726)) {
+		struct xe_reg_sr_entry sr_entry = {
+			.reg = ROW_CHICKEN,
+			.clr_bits = STALL_DOP_GATING_DISABLE,
+			.set_bits = STALL_DOP_GATING_DISABLE,
+			.read_mask = STALL_DOP_GATING_DISABLE,
+		};
+
+		xe_reg_sr_add(&hwe->reg_sr, &sr_entry, gt);
+	}
+
+	if (XE_WA(gt, 14015474168)) {
+		struct xe_reg_sr_entry sr_entry = {
+			.reg = ROW_CHICKEN2,
+			.clr_bits = XEHPC_DISABLE_BTB,
+			.set_bits = XEHPC_DISABLE_BTB,
+			.read_mask = XEHPC_DISABLE_BTB,
+		};
+
+		xe_reg_sr_add(&hwe->reg_sr, &sr_entry, gt);
+	}
+
+	if (xe->info.graphics_verx100 >= 1200) {
+		u32 mask = TD_CTL_BREAKPOINT_ENABLE |
+			   TD_CTL_FORCE_THREAD_BREAKPOINT_ENABLE |
+			   TD_CTL_FEH_AND_FEE_ENABLE;
+		struct xe_reg_sr_entry sr_entry = {
+			.reg = TD_CTL,
+			.clr_bits = mask,
+			.set_bits = mask,
+			.read_mask = mask,
+		};
+
+		xe_reg_sr_add(&hwe->reg_sr, &sr_entry, gt);
+	}
+
+	if (xe->info.graphics_verx100 >= 1250) {
+		struct xe_reg_sr_entry sr_entry = {
+			.reg = TD_CTL,
+			.clr_bits = TD_CTL_GLOBAL_DEBUG_ENABLE,
+			.set_bits = TD_CTL_GLOBAL_DEBUG_ENABLE,
+			.read_mask = TD_CTL_GLOBAL_DEBUG_ENABLE,
+		};
+
+		xe_reg_sr_add(&hwe->reg_sr, &sr_entry, gt);
+	}
+}
+
 void xe_eudebug_init(struct xe_device *xe)
 {
 	spin_lock_init(&xe->eudebug.lock);
diff --git a/drivers/gpu/drm/xe/xe_eudebug.h b/drivers/gpu/drm/xe/xe_eudebug.h
index 326ddbd50651..3cd6bc7bb682 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.h
+++ b/drivers/gpu/drm/xe/xe_eudebug.h
@@ -11,6 +11,7 @@ struct xe_device;
 struct xe_file;
 struct xe_vm;
 struct xe_exec_queue;
+struct xe_hw_engine;
 
 #if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
 
@@ -20,6 +21,7 @@ int xe_eudebug_connect_ioctl(struct drm_device *dev,
 
 void xe_eudebug_init(struct xe_device *xe);
 void xe_eudebug_fini(struct xe_device *xe);
+void xe_eudebug_init_hw_engine(struct xe_hw_engine *hwe);
 
 void xe_eudebug_file_open(struct xe_file *xef);
 void xe_eudebug_file_close(struct xe_file *xef);
@@ -38,6 +40,7 @@ static inline int xe_eudebug_connect_ioctl(struct drm_device *dev,
 
 static inline void xe_eudebug_init(struct xe_device *xe) { }
 static inline void xe_eudebug_fini(struct xe_device *xe) { }
+static inline void xe_eudebug_init_hw_engine(struct xe_hw_engine *hwe) { }
 
 static inline void xe_eudebug_file_open(struct xe_file *xef) { }
 static inline void xe_eudebug_file_close(struct xe_file *xef) { }
diff --git a/drivers/gpu/drm/xe/xe_hw_engine.c b/drivers/gpu/drm/xe/xe_hw_engine.c
index ea6d9ef7fab6..8e9a08395042 100644
--- a/drivers/gpu/drm/xe/xe_hw_engine.c
+++ b/drivers/gpu/drm/xe/xe_hw_engine.c
@@ -16,6 +16,7 @@
 #include "xe_assert.h"
 #include "xe_bo.h"
 #include "xe_device.h"
+#include "xe_eudebug.h"
 #include "xe_execlist.h"
 #include "xe_force_wake.h"
 #include "xe_gsc.h"
@@ -557,6 +558,7 @@ static void hw_engine_init_early(struct xe_gt *gt, struct xe_hw_engine *hwe,
 	xe_tuning_process_engine(hwe);
 	xe_wa_process_engine(hwe);
 	hw_engine_setup_default_state(hwe);
+	xe_eudebug_init_hw_engine(hwe);
 
 	xe_reg_sr_init(&hwe->reg_whitelist, hwe->name, gt_to_xe(gt));
 	xe_reg_whitelist_process_engine(hwe);
diff --git a/drivers/gpu/drm/xe/xe_wa_oob.rules b/drivers/gpu/drm/xe/xe_wa_oob.rules
index 920ca5060146..da7d2c84298c 100644
--- a/drivers/gpu/drm/xe/xe_wa_oob.rules
+++ b/drivers/gpu/drm/xe/xe_wa_oob.rules
@@ -37,3 +37,5 @@
 16023588340	GRAPHICS_VERSION(2001)
 14019789679	GRAPHICS_VERSION(1255)
 		GRAPHICS_VERSION_RANGE(1270, 2004)
+18022722726	GRAPHICS_VERSION_RANGE(1250, 1274)
+14015474168	PLATFORM(PVC)
-- 
2.34.1


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

* [PATCH 06/18] drm/xe: Add EUDEBUG_ENABLE exec queue property
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
                   ` (4 preceding siblings ...)
  2024-10-01 14:42 ` [PATCH 05/18] drm/xe/eudebug: hw enablement for eudebug Mika Kuoppala
@ 2024-10-01 14:42 ` Mika Kuoppala
  2024-10-01 14:42 ` [PATCH 07/18] drm/xe/eudebug: Introduce per device attention scan worker Mika Kuoppala
                   ` (14 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Mika Kuoppala @ 2024-10-01 14:42 UTC (permalink / raw)
  To: intel-xe; +Cc: Dominik Grzegorzek, Matthew Brost, Mika Kuoppala

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

Introduce exec queue immutable property of eudebug
with a flags as value to enable eudebug specific feature(s).

For now engine lrc will use this flag to set up runalone
hw feature. Runalone is used to ensure that only one hw engine
of group [rcs0, ccs0-3] is active on a tile.

Note: unlike the i915, xe allows user to set runalone
also on devices with single render/compute engine. It should not
make much difference, but leave control to the user.

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_execlist.c         |  2 +-
 drivers/gpu/drm/xe/xe_lrc.c              | 16 +++++++--
 drivers/gpu/drm/xe/xe_lrc.h              |  4 ++-
 include/uapi/drm/xe_drm.h                |  3 +-
 8 files changed, 74 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index f90b15fc4964..15a3f8b087a0 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -1279,7 +1279,7 @@ static int exec_queue_create_event(struct xe_eudebug *d,
 	u64 h_lrc[XE_HW_ENGINE_MAX_INSTANCE], seqno;
 	int i;
 
-	if (!xe_exec_queue_is_lr(q))
+	if (!xe_exec_queue_is_debuggable(q))
 		return 0;
 
 	h_c = find_handle(d->res, XE_EUDEBUG_RES_TYPE_CLIENT, xef);
@@ -1331,7 +1331,7 @@ static int exec_queue_destroy_event(struct xe_eudebug *d,
 	u64 h_lrc[XE_HW_ENGINE_MAX_INSTANCE], seqno;
 	int i;
 
-	if (!xe_exec_queue_is_lr(q))
+	if (!xe_exec_queue_is_debuggable(q))
 		return 0;
 
 	h_c = find_handle(d->res, XE_EUDEBUG_RES_TYPE_CLIENT, xef);
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
index e6b2487de48e..c2d6993ac103 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.c
+++ b/drivers/gpu/drm/xe/xe_exec_queue.c
@@ -109,6 +109,7 @@ static struct xe_exec_queue *__xe_exec_queue_alloc(struct xe_device *xe,
 static int __xe_exec_queue_init(struct xe_exec_queue *q)
 {
 	struct xe_vm *vm = q->vm;
+	u32 flags = 0;
 	int i, err;
 
 	if (vm) {
@@ -117,8 +118,11 @@ static int __xe_exec_queue_init(struct xe_exec_queue *q)
 			return err;
 	}
 
+	if (q->eudebug_flags & EXEC_QUEUE_EUDEBUG_FLAG_ENABLE)
+		flags |= LRC_CREATE_RUNALONE;
+
 	for (i = 0; i < q->width; ++i) {
-		q->lrc[i] = xe_lrc_create(q->hwe, q->vm, SZ_16K);
+		q->lrc[i] = xe_lrc_create(q->hwe, q->vm, SZ_16K, flags);
 		if (IS_ERR(q->lrc[i])) {
 			err = PTR_ERR(q->lrc[i]);
 			goto err_unlock;
@@ -393,6 +397,42 @@ static int exec_queue_set_timeslice(struct xe_device *xe, struct xe_exec_queue *
 	return 0;
 }
 
+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;
+
+	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);
@@ -400,6 +440,7 @@ typedef int (*xe_exec_queue_set_property_fn)(struct xe_device *xe,
 static const xe_exec_queue_set_property_fn exec_queue_set_property_funcs[] = {
 	[DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY] = exec_queue_set_priority,
 	[DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE] = exec_queue_set_timeslice,
+	[DRM_XE_EXEC_QUEUE_SET_PROPERTY_EUDEBUG] = exec_queue_set_eudebug,
 };
 
 static int exec_queue_user_ext_set_property(struct xe_device *xe,
@@ -419,7 +460,8 @@ static int exec_queue_user_ext_set_property(struct xe_device *xe,
 			 ARRAY_SIZE(exec_queue_set_property_funcs)) ||
 	    XE_IOCTL_DBG(xe, ext.pad) ||
 	    XE_IOCTL_DBG(xe, ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY &&
-			 ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE))
+			 ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE &&
+			 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 90c7f73eab88..421d8dc89814 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.h
+++ b/drivers/gpu/drm/xe/xe_exec_queue.h
@@ -85,4 +85,6 @@ int xe_exec_queue_last_fence_test_dep(struct xe_exec_queue *q,
 				      struct xe_vm *vm);
 void xe_exec_queue_update_run_ticks(struct xe_exec_queue *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 7deb480e26af..9eced5867f07 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue_types.h
+++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h
@@ -90,6 +90,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_execlist.c b/drivers/gpu/drm/xe/xe_execlist.c
index f3b71fe7a96d..b425c48ca49d 100644
--- a/drivers/gpu/drm/xe/xe_execlist.c
+++ b/drivers/gpu/drm/xe/xe_execlist.c
@@ -265,7 +265,7 @@ struct xe_execlist_port *xe_execlist_port_create(struct xe_device *xe,
 
 	port->hwe = hwe;
 
-	port->lrc = xe_lrc_create(hwe, NULL, SZ_16K);
+	port->lrc = xe_lrc_create(hwe, NULL, SZ_16K, 0);
 	if (IS_ERR(port->lrc)) {
 		err = PTR_ERR(port->lrc);
 		goto err;
diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c
index f0976230012a..68a411722f09 100644
--- a/drivers/gpu/drm/xe/xe_lrc.c
+++ b/drivers/gpu/drm/xe/xe_lrc.c
@@ -893,7 +893,7 @@ static void xe_lrc_finish(struct xe_lrc *lrc)
 #define PVC_CTX_ACC_CTR_THOLD	(0x2a + 1)
 
 static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
-		       struct xe_vm *vm, u32 ring_size)
+		       struct xe_vm *vm, u32 ring_size, u32 flags)
 {
 	struct xe_gt *gt = hwe->gt;
 	struct xe_tile *tile = gt_to_tile(gt);
@@ -1010,6 +1010,16 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
 	map = __xe_lrc_start_seqno_map(lrc);
 	xe_map_write32(lrc_to_xe(lrc), &map, lrc->fence_ctx.next_seqno - 1);
 
+	if (flags & 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:
@@ -1029,7 +1039,7 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
  * upon failure.
  */
 struct xe_lrc *xe_lrc_create(struct xe_hw_engine *hwe, struct xe_vm *vm,
-			     u32 ring_size)
+			     u32 ring_size, u32 flags)
 {
 	struct xe_lrc *lrc;
 	int err;
@@ -1038,7 +1048,7 @@ struct xe_lrc *xe_lrc_create(struct xe_hw_engine *hwe, struct xe_vm *vm,
 	if (!lrc)
 		return ERR_PTR(-ENOMEM);
 
-	err = xe_lrc_init(lrc, hwe, vm, ring_size);
+	err = xe_lrc_init(lrc, hwe, vm, ring_size, flags);
 	if (err) {
 		kfree(lrc);
 		return ERR_PTR(err);
diff --git a/drivers/gpu/drm/xe/xe_lrc.h b/drivers/gpu/drm/xe/xe_lrc.h
index c24542e89318..16a9264fdcfa 100644
--- a/drivers/gpu/drm/xe/xe_lrc.h
+++ b/drivers/gpu/drm/xe/xe_lrc.h
@@ -22,8 +22,10 @@ struct xe_vm;
 
 #define LRC_PPHWSP_SCRATCH_ADDR (0x34 * 4)
 
+#define LRC_CREATE_RUNALONE	BIT(0)
+
 struct xe_lrc *xe_lrc_create(struct xe_hw_engine *hwe, struct xe_vm *vm,
-			     u32 ring_size);
+			     u32 ring_size, u32 flags);
 void xe_lrc_destroy(struct kref *ref);
 
 /**
diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h
index 7e66e9117f49..985dcf852458 100644
--- a/include/uapi/drm/xe_drm.h
+++ b/include/uapi/drm/xe_drm.h
@@ -1110,7 +1110,8 @@ struct drm_xe_exec_queue_create {
 #define DRM_XE_EXEC_QUEUE_EXTENSION_SET_PROPERTY		0
 #define   DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY		0
 #define   DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE		1
-
+#define   DRM_XE_EXEC_QUEUE_SET_PROPERTY_EUDEBUG		2
+#define     DRM_XE_EXEC_QUEUE_EUDEBUG_FLAG_ENABLE		(1 << 0)
 	/** @extensions: Pointer to the first extension struct, if any */
 	__u64 extensions;
 
-- 
2.34.1


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

* [PATCH 07/18] drm/xe/eudebug: Introduce per device attention scan worker
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
                   ` (5 preceding siblings ...)
  2024-10-01 14:42 ` [PATCH 06/18] drm/xe: Add EUDEBUG_ENABLE exec queue property Mika Kuoppala
@ 2024-10-01 14:42 ` Mika Kuoppala
  2024-10-01 14:42 ` [PATCH 08/18] drm/xe/eudebug: Introduce EU control interface Mika Kuoppala
                   ` (13 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Mika Kuoppala @ 2024-10-01 14:42 UTC (permalink / raw)
  To: intel-xe
  Cc: Dominik Grzegorzek, Christoph Manszewski, Maciej Patelczyk,
	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

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>
---
 drivers/gpu/drm/xe/Makefile              |   1 +
 drivers/gpu/drm/xe/regs/xe_engine_regs.h |   3 +
 drivers/gpu/drm/xe/regs/xe_gt_regs.h     |   7 +
 drivers/gpu/drm/xe/xe_device.c           |   2 +
 drivers/gpu/drm/xe/xe_device_types.h     |   3 +
 drivers/gpu/drm/xe/xe_eudebug.c          | 381 ++++++++++++++++++++++-
 drivers/gpu/drm/xe/xe_eudebug.h          |   2 +
 drivers/gpu/drm/xe/xe_eudebug_types.h    |  32 ++
 drivers/gpu/drm/xe/xe_gt_debug.c         | 148 +++++++++
 drivers/gpu/drm/xe/xe_gt_debug.h         |  21 ++
 include/uapi/drm/xe_drm_eudebug.h        |  13 +
 11 files changed, 612 insertions(+), 1 deletion(-)
 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 a3cd467cdaa6..660c1dbba8d0 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -48,6 +48,7 @@ xe-y += xe_bb.o \
 	xe_gt_clock.o \
 	xe_gt_freq.o \
 	xe_gt_idle.o \
+	xe_gt_debug.o \
 	xe_gt_mcr.o \
 	xe_gt_pagefault.o \
 	xe_gt_sysfs.o \
diff --git a/drivers/gpu/drm/xe/regs/xe_engine_regs.h b/drivers/gpu/drm/xe/regs/xe_engine_regs.h
index e004423a66bf..f2f136b1b0fa 100644
--- a/drivers/gpu/drm/xe/regs/xe_engine_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_engine_regs.h
@@ -133,6 +133,9 @@
 #define RING_EXECLIST_STATUS_LO(base)		XE_REG((base) + 0x234)
 #define RING_EXECLIST_STATUS_HI(base)		XE_REG((base) + 0x234 + 4)
 
+#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_OAC_CONTEXT_ENABLE		REG_BIT(8)
 #define	  CTX_CTRL_RUN_ALONE			REG_BIT(7)
diff --git a/drivers/gpu/drm/xe/regs/xe_gt_regs.h b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
index 2780541feb67..0129480dbd9d 100644
--- a/drivers/gpu/drm/xe/regs/xe_gt_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
@@ -465,6 +465,8 @@
 #define   DISABLE_ECC				REG_BIT(5)
 #define   ENABLE_PREFETCH_INTO_IC		REG_BIT(3)
 
+#define TD_ATT(x)				XE_REG_MCR(0xe470 + (x) * 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)
@@ -545,6 +547,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 FORCEWAKE_ACK_GT			XE_REG(0x130044)
 
 /* Applicable for all FORCEWAKE_DOMAIN and FORCEWAKE_ACK_DOMAIN regs */
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index ec5eedbbf320..77049c56c26c 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -776,6 +776,8 @@ int xe_device_probe(struct xe_device *xe)
 
 	xe_debugfs_register(xe);
 
+	xe_eudebug_init_late(xe);
+
 	xe_hwmon_register(xe);
 
 	for_each_gt(gt, xe, id)
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index 54ceeee7cf75..562cc8930533 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -548,6 +548,9 @@ struct xe_device {
 
 		/** discovery_lock: used for discovery to block xe ioctls */
 		struct rw_semaphore discovery_lock;
+
+		/** @attention_scan: attention scan worker */
+		struct delayed_work attention_scan;
 	} eudebug;
 #endif
 
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index 15a3f8b087a0..633e9043f603 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -20,9 +20,17 @@
 #include "xe_eudebug.h"
 #include "xe_eudebug_types.h"
 #include "xe_exec_queue.h"
+#include "xe_force_wake.h"
+#include "xe_gt.h"
+#include "xe_gt_debug.h"
+#include "xe_hw_engine.h"
+#include "xe_lrc.h"
 #include "xe_macros.h"
+#include "xe_mmio.h"
+#include "xe_pm.h"
 #include "xe_reg_sr.h"
 #include "xe_rtp.h"
+#include "xe_sched_job.h"
 #include "xe_vm.h"
 #include "xe_wa.h"
 
@@ -757,7 +765,7 @@ static long xe_eudebug_read_event(struct xe_eudebug *d,
 		u64_to_user_ptr(arg);
 	struct drm_xe_eudebug_event user_event;
 	struct xe_eudebug_event *event;
-	const unsigned int max_event = DRM_XE_EUDEBUG_EVENT_EXEC_QUEUE;
+	const unsigned int max_event = DRM_XE_EUDEBUG_EVENT_EU_ATTENTION;
 	long ret = 0;
 
 	if (XE_IOCTL_DBG(xe, copy_from_user(&user_event, user_orig, sizeof(user_event))))
@@ -865,6 +873,365 @@ static const struct file_operations fops = {
 	.unlocked_ioctl	= xe_eudebug_ioctl,
 };
 
+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)
+{
+	int ret;
+
+	ret = xe_force_wake_get(gt_to_fw(hwe->gt), hwe->domain);
+	if (ret)
+		return ret;
+
+	ret = __current_lrca(hwe, lrc_hw);
+
+	xe_force_wake_put(gt_to_fw(hwe->gt), hwe->domain);
+
+	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 u32 engine_status(const struct xe_hw_engine * const hwe,
+			 u32 rcu_debug1)
+{
+	const bool xe1 = GRAPHICS_VER(gt_to_xe(hwe->gt)) < 20;
+	unsigned int shift;
+
+	if (hwe->class == XE_ENGINE_CLASS_RENDER) {
+		shift = 7;
+		XE_WARN_ON(hwe->instance != 0);
+	} else if (hwe->class == XE_ENGINE_CLASS_COMPUTE) {
+		XE_WARN_ON(hwe->instance > 3);
+
+		if (xe1)
+			shift = 10 + (hwe->instance * 3);
+		else
+			shift = 11 + (hwe->instance * 4);
+	} else {
+		XE_WARN_ON(hwe->class);
+		return 0;
+	}
+
+	return (rcu_debug1 >> shift) & RCU_DEBUG_1_ENGINE_STATUS;
+}
+
+static bool engine_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_context_set(const struct xe_hw_engine * const hwe,
+			       u32 rcu_debug1)
+{
+	return engine_status(hwe, rcu_debug1) & RCU_DEBUG_1_CONTEXT_ACTIVE;
+}
+
+static bool engine_has_runalone(const struct xe_hw_engine * const hwe)
+{
+	return hwe->class == XE_ENGINE_CLASS_RENDER ||
+		hwe->class == XE_ENGINE_CLASS_COMPUTE;
+}
+
+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;
+	u32 val;
+
+	if (xe_force_wake_get(gt_to_fw(gt), XE_FW_GT)) {
+		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), XE_FW_GT);
+
+	drm_dbg(&gt_to_xe(gt)->drm, "eudbg: runalone RCU_DEBUG_1 = 0x%08x\n", val);
+
+	num_active = 0;
+	for_each_hw_engine(hwe, gt, id) {
+		bool runalone, ctx;
+
+		if (!engine_has_runalone(hwe))
+			continue;
+
+		runalone = engine_runalone_set(hwe, val);
+		ctx = engine_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 *runalone_active_queue_get(struct xe_gt *gt, int *lrc_idx)
+{
+	struct xe_device *xe = gt_to_xe(gt);
+	struct xe_exec_queue *q, *found = NULL;
+	struct xe_hw_engine *active;
+	struct xe_file *xef;
+	unsigned long i;
+	int idx, err;
+	u32 lrc_hw;
+
+	active = get_runalone_active_hw_engine(gt);
+	if (!active) {
+		drm_dbg(&gt_to_xe(gt)->drm, "Runalone engine not found!");
+		return ERR_PTR(-ENOENT);
+	}
+
+	err = current_lrca(active, &lrc_hw);
+	if (err)
+		return ERR_PTR(err);
+
+	/* Take write so that we can safely check the lists */
+	down_write(&xe->eudebug.discovery_lock);
+	list_for_each_entry(xef, &xe->clients.list, eudebug.client_link) {
+		xa_for_each(&xef->exec_queue.xa, i, q) {
+			if (q->gt != gt)
+				continue;
+
+			if (q->class != active->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;
+		}
+
+		if (found)
+			break;
+	}
+	up_write(&xe->eudebug.discovery_lock);
+
+	if (!found)
+		return ERR_PTR(-ENOENT);
+
+	if (XE_WARN_ON(current_lrca(active, &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 int send_attention_event(struct xe_eudebug *d, struct xe_exec_queue *q, int lrc_idx)
+{
+	struct xe_eudebug_event_eu_attention *ea;
+	struct xe_eudebug_event *event;
+	int h_c, h_queue, h_lrc;
+	u32 size = xe_gt_eu_attention_bitmap_size(q->gt);
+	u32 sz = struct_size(ea, bitmask, size);
+	int ret;
+
+	XE_WARN_ON(lrc_idx < 0 || lrc_idx >= q->width);
+
+	XE_WARN_ON(!xe_exec_queue_is_debuggable(q));
+
+	h_c = find_handle(d->res, XE_EUDEBUG_RES_TYPE_CLIENT, q->vm->xef);
+	if (h_c < 0)
+		return h_c;
+
+	h_queue = find_handle(d->res, XE_EUDEBUG_RES_TYPE_EXEC_QUEUE, q);
+	if (h_queue < 0)
+		return h_queue;
+
+	h_lrc = find_handle(d->res, XE_EUDEBUG_RES_TYPE_LRC, q->lrc[lrc_idx]);
+	if (h_lrc < 0)
+		return h_lrc;
+
+	event = __xe_eudebug_create_event(d, 0, DRM_XE_EUDEBUG_EVENT_EU_ATTENTION,
+					  DRM_XE_EUDEBUG_EVENT_STATE_CHANGE, sz, GFP_KERNEL);
+
+	if (!event)
+		return -ENOSPC;
+
+	ea = cast_event(ea, event);
+	write_member(struct drm_xe_eudebug_event_eu_attention, ea, client_handle, (u64)h_c);
+	write_member(struct drm_xe_eudebug_event_eu_attention, ea, exec_queue_handle, (u64)h_queue);
+	write_member(struct drm_xe_eudebug_event_eu_attention, ea, lrc_handle, (u64)h_lrc);
+	write_member(struct drm_xe_eudebug_event_eu_attention, ea, bitmask_size, size);
+
+	mutex_lock(&d->eu_lock);
+	event->seqno = atomic_long_inc_return(&d->events.seqno);
+	ret = xe_gt_eu_attention_bitmap(q->gt, &ea->bitmask[0], ea->bitmask_size);
+	mutex_unlock(&d->eu_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;
+
+	if (list_empty_careful(&gt_to_xe(gt)->eudebug.list))
+		return -ENOTCONN;
+
+	q = 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(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;
+}
+
+#define XE_EUDEBUG_ATTENTION_INTERVAL 100
+static void attention_scan_fn(struct work_struct *work)
+{
+	struct xe_device *xe = container_of(work, typeof(*xe), eudebug.attention_scan.work);
+	long delay = msecs_to_jiffies(XE_EUDEBUG_ATTENTION_INTERVAL);
+	struct xe_gt *gt;
+	u8 gt_id;
+
+	if (list_empty_careful(&xe->eudebug.list))
+		delay *= 10;
+
+	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_scan, delay);
+}
+
+static void attention_scan_cancel(struct xe_device *xe)
+{
+	cancel_delayed_work_sync(&xe->eudebug.attention_scan);
+}
+
+static void attention_scan_flush(struct xe_device *xe)
+{
+	mod_delayed_work(system_wq, &xe->eudebug.attention_scan, 0);
+}
+
 static void discovery_work_fn(struct work_struct *work);
 
 static int
@@ -899,6 +1266,7 @@ xe_eudebug_connect(struct xe_device *xe,
 
 	kref_init(&d->ref);
 	spin_lock_init(&d->connection.lock);
+	mutex_init(&d->eu_lock);
 	init_waitqueue_head(&d->events.write_done);
 	init_waitqueue_head(&d->events.read_done);
 	init_completion(&d->discovery);
@@ -925,6 +1293,7 @@ xe_eudebug_connect(struct xe_device *xe,
 
 	kref_get(&d->ref);
 	queue_work(xe->eudebug.ordered_wq, &d->discovery_work);
+	attention_scan_flush(xe);
 
 	eu_dbg(d, "connected session %lld", d->session);
 
@@ -1021,13 +1390,23 @@ void xe_eudebug_init(struct xe_device *xe)
 	INIT_LIST_HEAD(&xe->eudebug.list);
 	INIT_LIST_HEAD(&xe->clients.list);
 	init_rwsem(&xe->eudebug.discovery_lock);
+	INIT_DELAYED_WORK(&xe->eudebug.attention_scan, attention_scan_fn);
 
 	xe->eudebug.ordered_wq = alloc_ordered_workqueue("xe-eudebug-ordered-wq", 0);
 	xe->eudebug.available = !!xe->eudebug.ordered_wq;
 }
 
+void xe_eudebug_init_late(struct xe_device *xe)
+{
+	if (!xe->eudebug.available)
+		return;
+
+	attention_scan_flush(xe);
+}
+
 void xe_eudebug_fini(struct xe_device *xe)
 {
+	attention_scan_cancel(xe);
 	xe_assert(xe, list_empty_careful(&xe->eudebug.list));
 
 	if (xe->eudebug.ordered_wq)
diff --git a/drivers/gpu/drm/xe/xe_eudebug.h b/drivers/gpu/drm/xe/xe_eudebug.h
index 3cd6bc7bb682..1fe86bec99e1 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.h
+++ b/drivers/gpu/drm/xe/xe_eudebug.h
@@ -20,6 +20,7 @@ int xe_eudebug_connect_ioctl(struct drm_device *dev,
 			     struct drm_file *file);
 
 void xe_eudebug_init(struct xe_device *xe);
+void xe_eudebug_init_late(struct xe_device *xe);
 void xe_eudebug_fini(struct xe_device *xe);
 void xe_eudebug_init_hw_engine(struct xe_hw_engine *hwe);
 
@@ -39,6 +40,7 @@ static inline int xe_eudebug_connect_ioctl(struct drm_device *dev,
 					   struct drm_file *file) { return 0; }
 
 static inline void xe_eudebug_init(struct xe_device *xe) { }
+static inline void xe_eudebug_init_late(struct xe_device *xe) { }
 static inline void xe_eudebug_fini(struct xe_device *xe) { }
 static inline void xe_eudebug_init_hw_engine(struct xe_hw_engine *hwe) { }
 
diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
index 6428905a0557..b885296fddbb 100644
--- a/drivers/gpu/drm/xe/xe_eudebug_types.h
+++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
@@ -105,6 +105,9 @@ struct xe_eudebug {
 	/** @discovery_work: worker to discover resources for target_task */
 	struct work_struct discovery_work;
 
+	/** eu_lock: guards operations on eus (eu thread control and attention) */
+	struct mutex eu_lock;
+
 	/** @events: kfifo queue of to-be-delivered events */
 	struct {
 		/** @lock: guards access to fifo */
@@ -202,4 +205,33 @@ struct xe_eudebug_event_exec_queue {
 	u64 lrc_handle[];
 };
 
+/**
+ * struct xe_eudebug_event_eu_attention - Internal event for EU attention
+ */
+struct xe_eudebug_event_eu_attention {
+	/** @base: base event */
+	struct xe_eudebug_event base;
+
+	/** @client_handle: client for the attention */
+	u64 client_handle;
+
+	/** @exec_queue_handle: handle of exec_queue which raised attention */
+	u64 exec_queue_handle;
+
+	/** @lrc_handle: lrc handle of the workload which raised attention */
+	u64 lrc_handle;
+
+	/** @flags: eu attention event flags, currently MBZ */
+	u32 flags;
+
+	/** @bitmask_size: size of the bitmask, specific to device */
+	u32 bitmask_size;
+
+	/**
+	 * @bitmask: reflects threads currently signalling attention,
+	 * starting from natural hardware order of DSS=0, eu=0
+	 */
+	u8 bitmask[];
+};
+
 #endif
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..d36684c7514d
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_debug.c
@@ -0,0 +1,148 @@
+// 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"
+
+static int xe_gt_foreach_dss_group_instance(struct xe_gt *gt,
+					    int (*fn)(struct xe_gt *gt,
+						      void *data,
+						      u16 group,
+						      u16 instance),
+					    void *data)
+{
+	const enum xe_force_wake_domains fw_domains = XE_FW_GT | XE_FW_RENDER;
+	unsigned int dss;
+	u16 group, instance;
+	int ret;
+
+	ret = xe_force_wake_get(gt_to_fw(gt), fw_domains);
+	if (ret)
+		return ret;
+
+	for_each_dss_steering(dss, gt, group, instance) {
+		ret = fn(gt, data, group, instance);
+		if (ret)
+			break;
+	}
+
+	xe_force_wake_put(gt_to_fw(gt), fw_domains);
+
+	return ret;
+}
+
+static int read_first_attention_mcr(struct xe_gt *gt, void *data,
+				    u16 group, u16 instance)
+{
+	unsigned int row;
+
+	for (row = 0; row < 2; row++) {
+		u32 val;
+
+		val = xe_gt_mcr_unicast_read(gt, TD_ATT(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  bitmap_weight(dss_mask, XE_MAX_DSS_FUSE_BITS) *
+		TD_EU_ATTENTION_MAX_ROWS * 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)
+{
+	struct attn_read_iter * const iter = data;
+	unsigned int row;
+
+	for (row = 0; row < TD_EU_ATTENTION_MAX_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));
+
+		val = xe_gt_mcr_unicast_read(gt, TD_ATT(row), group, instance);
+
+		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..3f13dbb17a5f
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_debug.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#ifndef __XE_GT_DEBUG_
+#define __XE_GT_DEBUG_
+
+#define TD_EU_ATTENTION_MAX_ROWS 2u
+
+#include "xe_gt_types.h"
+
+#define XE_GT_ATTENTION_TIMEOUT_MS 100
+
+int xe_gt_eu_threads_needing_attention(struct xe_gt *gt);
+
+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/include/uapi/drm/xe_drm_eudebug.h b/include/uapi/drm/xe_drm_eudebug.h
index ac44e890152a..1e63bdede5f2 100644
--- a/include/uapi/drm/xe_drm_eudebug.h
+++ b/include/uapi/drm/xe_drm_eudebug.h
@@ -27,12 +27,14 @@ struct drm_xe_eudebug_event {
 #define DRM_XE_EUDEBUG_EVENT_OPEN		2
 #define DRM_XE_EUDEBUG_EVENT_VM			3
 #define DRM_XE_EUDEBUG_EVENT_EXEC_QUEUE		4
+#define DRM_XE_EUDEBUG_EVENT_EU_ATTENTION	5
 
 	__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)
+
 	__u64 seqno;
 	__u64 reserved;
 };
@@ -61,6 +63,17 @@ struct drm_xe_eudebug_event_exec_queue {
 	__u64 lrc_handle[];
 };
 
+struct drm_xe_eudebug_event_eu_attention {
+	struct drm_xe_eudebug_event base;
+
+	__u64 client_handle;
+	__u64 exec_queue_handle;
+	__u64 lrc_handle;
+	__u32 flags;
+	__u32 bitmask_size;
+	__u8 bitmask[];
+};
+
 #if defined(__cplusplus)
 }
 #endif
-- 
2.34.1


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

* [PATCH 08/18] drm/xe/eudebug: Introduce EU control interface
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
                   ` (6 preceding siblings ...)
  2024-10-01 14:42 ` [PATCH 07/18] drm/xe/eudebug: Introduce per device attention scan worker Mika Kuoppala
@ 2024-10-01 14:42 ` Mika Kuoppala
  2024-10-01 14:42 ` [PATCH 09/18] drm/xe/eudebug: Add vm bind and vm bind ops Mika Kuoppala
                   ` (12 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Mika Kuoppala @ 2024-10-01 14:42 UTC (permalink / raw)
  To: intel-xe; +Cc: Dominik Grzegorzek, Maciej Patelczyk, 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)

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>
---
 drivers/gpu/drm/xe/regs/xe_gt_regs.h  |   2 +
 drivers/gpu/drm/xe/xe_eudebug.c       | 516 +++++++++++++++++++++++++-
 drivers/gpu/drm/xe/xe_eudebug_types.h |  24 ++
 drivers/gpu/drm/xe/xe_gt_debug.c      |  12 +-
 drivers/gpu/drm/xe/xe_gt_debug.h      |   6 +
 include/uapi/drm/xe_drm_eudebug.h     |  21 +-
 6 files changed, 561 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/xe/regs/xe_gt_regs.h b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
index 0129480dbd9d..a6225e4a8516 100644
--- a/drivers/gpu/drm/xe/regs/xe_gt_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
@@ -476,6 +476,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 TD_CLR(i)				XE_REG_MCR(0xe490 + (i) * 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)
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index 633e9043f603..ac41c2ac8001 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -23,6 +23,7 @@
 #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"
@@ -587,6 +588,64 @@ static int find_handle(struct xe_eudebug_resources *res,
 	return id;
 }
 
+static void *find_resource__unlocked(struct xe_eudebug_resources *res,
+				     const int type,
+				     const u32 id)
+{
+	struct xe_eudebug_resource *r;
+	struct xe_eudebug_handle *h;
+
+	r = resource_from_type(res, type);
+	h = xa_load(&r->xa, id);
+
+	return h ? (void *)(uintptr_t)h->key : NULL;
+}
+
+static void *find_resource(struct xe_eudebug_resources *res,
+			   const int type,
+			   const u32 id)
+{
+	void *p;
+
+	mutex_lock(&res->lock);
+	p =  find_resource__unlocked(res, type, id);
+	mutex_unlock(&res->lock);
+
+	return p;
+}
+
+static struct xe_file *find_client_get(struct xe_eudebug *d, const u32 id)
+{
+	struct xe_file *xef;
+
+	mutex_lock(&d->res->lock);
+	xef = find_resource__unlocked(d->res, XE_EUDEBUG_RES_TYPE_CLIENT, id);
+	if (xef)
+		xe_file_get(xef);
+	mutex_unlock(&d->res->lock);
+
+	return xef;
+}
+
+static struct xe_exec_queue *find_exec_queue_get(struct xe_eudebug *d,
+						 u32 id)
+{
+	struct xe_exec_queue *q;
+
+	mutex_lock(&d->res->lock);
+	q = find_resource__unlocked(d->res, XE_EUDEBUG_RES_TYPE_EXEC_QUEUE, id);
+	if (q)
+		xe_exec_queue_get(q);
+	mutex_unlock(&d->res->lock);
+
+	return q;
+}
+
+static struct xe_lrc *find_lrc(struct xe_eudebug *d, const u32 id)
+{
+	return find_resource(d->res, XE_EUDEBUG_RES_TYPE_LRC, id);
+}
+
 static int _xe_eudebug_add_handle(struct xe_eudebug *d,
 				  int type,
 				  void *p,
@@ -840,6 +899,177 @@ static long xe_eudebug_read_event(struct xe_eudebug *d,
 	return ret;
 }
 
+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_file *xef;
+	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;
+
+	xef = find_client_get(d, arg->client_handle);
+	if (XE_IOCTL_DBG(xe, !xef))
+		return -EINVAL;
+
+	q = find_exec_queue_get(d, arg->exec_queue_handle);
+	if (XE_IOCTL_DBG(xe, !q)) {
+		xe_file_put(xef);
+		return -EINVAL;
+	}
+
+	if (XE_IOCTL_DBG(xe, !xe_exec_queue_is_debuggable(q))) {
+		ret = -EINVAL;
+		goto queue_put;
+	}
+
+	if (XE_IOCTL_DBG(xe, xef != q->vm->xef)) {
+		ret = -EINVAL;
+		goto queue_put;
+	}
+
+	lrc = 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->eu_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->eu_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);
+	xe_file_put(xef);
+
+	return ret;
+}
+
+static 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;
+	struct xe_file *xef;
+	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: client_handle=%llu, cmd=%u, flags=0x%x, exec_queue_handle=%llu, bitmask_size=%u\n",
+	       user_arg.client_handle, user_arg.cmd, user_arg.flags, user_arg.exec_queue_handle,
+	       user_arg.bitmask_size);
+
+	xef = find_client_get(d, user_arg.client_handle);
+	if (XE_IOCTL_DBG(xe, !xef))
+		return -EINVAL; /* As this is user input */
+
+	ret = do_eu_control(d, &user_arg, user_ptr);
+
+	xe_file_put(xef);
+
+	eu_dbg(d,
+	       "eu_control: client_handle=%llu, cmd=%u, flags=0x%x, exec_queue_handle=%llu, bitmask_size=%u ret=%d\n",
+	       user_arg.client_handle, user_arg.cmd, user_arg.flags, user_arg.exec_queue_handle,
+	       user_arg.bitmask_size, ret);
+
+	return ret;
+}
+
 static long xe_eudebug_ioctl(struct file *file,
 			     unsigned int cmd,
 			     unsigned long arg)
@@ -856,6 +1086,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_EU_CONTROL:
+		ret = xe_eudebug_eu_control(d, arg);
+		eu_dbg(d, "ioctl cmd=EU_CONTROL ret=%ld\n", ret);
+		break;
 
 	default:
 		ret = -EINVAL;
@@ -1014,23 +1248,17 @@ static struct xe_hw_engine *get_runalone_active_hw_engine(struct xe_gt *gt)
 	return first;
 }
 
-static struct xe_exec_queue *runalone_active_queue_get(struct xe_gt *gt, int *lrc_idx)
+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(gt);
+	struct xe_device *xe = gt_to_xe(hwe->gt);
+	struct xe_gt *gt = hwe->gt;
 	struct xe_exec_queue *q, *found = NULL;
-	struct xe_hw_engine *active;
 	struct xe_file *xef;
 	unsigned long i;
 	int idx, err;
 	u32 lrc_hw;
 
-	active = get_runalone_active_hw_engine(gt);
-	if (!active) {
-		drm_dbg(&gt_to_xe(gt)->drm, "Runalone engine not found!");
-		return ERR_PTR(-ENOENT);
-	}
-
-	err = current_lrca(active, &lrc_hw);
+	err = current_lrca(hwe, &lrc_hw);
 	if (err)
 		return ERR_PTR(err);
 
@@ -1041,7 +1269,7 @@ static struct xe_exec_queue *runalone_active_queue_get(struct xe_gt *gt, int *lr
 			if (q->gt != gt)
 				continue;
 
-			if (q->class != active->class)
+			if (q->class != hwe->class)
 				continue;
 
 			if (xe_exec_queue_is_idle(q))
@@ -1067,7 +1295,7 @@ static struct xe_exec_queue *runalone_active_queue_get(struct xe_gt *gt, int *lr
 	if (!found)
 		return ERR_PTR(-ENOENT);
 
-	if (XE_WARN_ON(current_lrca(active, &lrc_hw)) &&
+	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);
@@ -1076,6 +1304,19 @@ static struct xe_exec_queue *runalone_active_queue_get(struct xe_gt *gt, int *lr
 	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 send_attention_event(struct xe_eudebug *d, struct xe_exec_queue *q, int lrc_idx)
 {
 	struct xe_eudebug_event_eu_attention *ea;
@@ -1124,7 +1365,6 @@ static int send_attention_event(struct xe_eudebug *d, struct xe_exec_queue *q, i
 	return xe_eudebug_queue_event(d, event);
 }
 
-
 static int xe_send_gt_attention(struct xe_gt *gt)
 {
 	struct xe_eudebug *d;
@@ -1232,6 +1472,255 @@ static void attention_scan_flush(struct xe_device *xe)
 	mod_delayed_work(system_wq, &xe->eudebug.attention_scan, 0);
 }
 
+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;
+	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)) {
+		ret = -EINVAL;
+		goto put_fw;
+	}
+
+	ret = xe_force_wake_get(gt_to_fw(gt), hwe->domain);
+	if (ret)
+		goto put_fw;
+
+	/* 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), q->hwe->domain);
+
+	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)
+{
+	struct ss_iter *iter = data;
+	struct xe_eudebug *d = iter->debugger;
+	unsigned int row;
+
+	for (row = 0; row < TD_EU_ATTENTION_MAX_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);
+
+		cur = xe_gt_mcr_unicast_read(gt, TD_ATT(row), group, instance);
+
+		if ((val | cur) != cur) {
+			eu_dbg(d,
+			       "WRONG CLEAR (%u:%u:%u) TD_CRL: 0x%08x; TD_ATT: 0x%08x\n",
+			       group, instance, row, val, cur);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int clear_attn_mcr(struct xe_gt *gt, void *data,
+			  u16 group, u16 instance)
+{
+	struct ss_iter *iter = data;
+	struct xe_eudebug *d = iter->debugger;
+	unsigned int row;
+
+	for (row = 0; row < TD_EU_ATTENTION_MAX_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;
+
+		xe_gt_mcr_unicast_write(gt, TD_CLR(row), val,
+					group, instance);
+
+		eu_dbg(d,
+		       "TD_CLR: (%u:%u:%u): 0x%08x\n",
+		       group, instance, 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,
+};
+
 static void discovery_work_fn(struct work_struct *work);
 
 static int
@@ -1291,6 +1780,7 @@ xe_eudebug_connect(struct xe_device *xe,
 		goto err_detach;
 	}
 
+	d->ops = &eu_control;
 	kref_get(&d->ref);
 	queue_work(xe->eudebug.ordered_wq, &d->discovery_work);
 	attention_scan_flush(xe);
diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
index b885296fddbb..73b3f906da66 100644
--- a/drivers/gpu/drm/xe/xe_eudebug_types.h
+++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
@@ -18,8 +18,12 @@
 
 struct xe_device;
 struct task_struct;
+struct xe_eudebug;
 struct xe_eudebug_event;
+struct xe_hw_engine;
 struct workqueue_struct;
+struct xe_exec_queue;
+struct xe_lrc;
 
 #define CONFIG_DRM_XE_DEBUGGER_EVENT_QUEUE_SIZE 64
 
@@ -65,6 +69,24 @@ struct xe_eudebug_resources {
 	struct xe_eudebug_resource rt[XE_EUDEBUG_RES_TYPE_COUNT];
 };
 
+/**
+ * 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
  */
@@ -128,6 +150,8 @@ struct xe_eudebug {
 		atomic_long_t seqno;
 	} events;
 
+	/** @ops operations for eu_control */
+	struct xe_eudebug_eu_control_ops *ops;
 };
 
 /**
diff --git a/drivers/gpu/drm/xe/xe_gt_debug.c b/drivers/gpu/drm/xe/xe_gt_debug.c
index d36684c7514d..fcf32c7c77d8 100644
--- a/drivers/gpu/drm/xe/xe_gt_debug.c
+++ b/drivers/gpu/drm/xe/xe_gt_debug.c
@@ -13,12 +13,12 @@
 #include "xe_pm.h"
 #include "xe_macros.h"
 
-static int xe_gt_foreach_dss_group_instance(struct xe_gt *gt,
-					    int (*fn)(struct xe_gt *gt,
-						      void *data,
-						      u16 group,
-						      u16 instance),
-					    void *data)
+int xe_gt_foreach_dss_group_instance(struct xe_gt *gt,
+				     int (*fn)(struct xe_gt *gt,
+					       void *data,
+					       u16 group,
+					       u16 instance),
+				     void *data)
 {
 	const enum xe_force_wake_domains fw_domains = XE_FW_GT | XE_FW_RENDER;
 	unsigned int dss;
diff --git a/drivers/gpu/drm/xe/xe_gt_debug.h b/drivers/gpu/drm/xe/xe_gt_debug.h
index 3f13dbb17a5f..342082699ff6 100644
--- a/drivers/gpu/drm/xe/xe_gt_debug.h
+++ b/drivers/gpu/drm/xe/xe_gt_debug.h
@@ -13,6 +13,12 @@
 #define XE_GT_ATTENTION_TIMEOUT_MS 100
 
 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),
+				     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,
diff --git a/include/uapi/drm/xe_drm_eudebug.h b/include/uapi/drm/xe_drm_eudebug.h
index 1e63bdede5f2..3c43aefefedd 100644
--- a/include/uapi/drm/xe_drm_eudebug.h
+++ b/include/uapi/drm/xe_drm_eudebug.h
@@ -15,7 +15,8 @@ extern "C" {
  *
  * This ioctl is available in debug version 1.
  */
-#define DRM_XE_EUDEBUG_IOCTL_READ_EVENT _IO('j', 0x0)
+#define DRM_XE_EUDEBUG_IOCTL_READ_EVENT		_IO('j', 0x0)
+#define DRM_XE_EUDEBUG_IOCTL_EU_CONTROL		_IOWR('j', 0x2, struct drm_xe_eudebug_eu_control)
 
 /* XXX: Document events to match their internal counterparts when moved to xe_drm.h */
 struct drm_xe_eudebug_event {
@@ -74,6 +75,24 @@ struct drm_xe_eudebug_event_eu_attention {
 	__u8 bitmask[];
 };
 
+struct drm_xe_eudebug_eu_control {
+	__u64 client_handle;
+
+#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;
+	__u32 flags;
+
+	__u64 seqno;
+
+	__u64 exec_queue_handle;
+	__u64 lrc_handle;
+	__u32 reserved;
+	__u32 bitmask_size;
+	__u64 bitmask_ptr;
+};
+
 #if defined(__cplusplus)
 }
 #endif
-- 
2.34.1


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

* [PATCH 09/18] drm/xe/eudebug: Add vm bind and vm bind ops
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
                   ` (7 preceding siblings ...)
  2024-10-01 14:42 ` [PATCH 08/18] drm/xe/eudebug: Introduce EU control interface Mika Kuoppala
@ 2024-10-01 14:42 ` Mika Kuoppala
  2024-10-01 14:42 ` [PATCH 10/18] drm/xe/eudebug: Add UFENCE events with acks Mika Kuoppala
                   ` (11 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Mika Kuoppala @ 2024-10-01 14:42 UTC (permalink / raw)
  To: intel-xe; +Cc: Christoph Manszewski, Mika Kuoppala

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

Add events dedicated to track vma bind and vma unbind operations. The
events are generated for operations performed on xe_vma MAP and UNMAP
for boss and userptrs.

As one bind can result in multiple operations and fail in the middle,
we want to store the events until full successful chain of operations
can be relayed to debugger.

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>
---
 drivers/gpu/drm/xe/xe_eudebug.c       | 316 +++++++++++++++++++++++++-
 drivers/gpu/drm/xe/xe_eudebug.h       |  13 ++
 drivers/gpu/drm/xe/xe_eudebug_types.h |  29 +++
 drivers/gpu/drm/xe/xe_vm.c            |  16 +-
 drivers/gpu/drm/xe/xe_vm_types.h      |  13 ++
 include/uapi/drm/xe_drm_eudebug.h     |  64 ++++++
 6 files changed, 448 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index ac41c2ac8001..785658b2e199 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -824,7 +824,7 @@ static long xe_eudebug_read_event(struct xe_eudebug *d,
 		u64_to_user_ptr(arg);
 	struct drm_xe_eudebug_event user_event;
 	struct xe_eudebug_event *event;
-	const unsigned int max_event = DRM_XE_EUDEBUG_EVENT_EU_ATTENTION;
+	const unsigned int max_event = DRM_XE_EUDEBUG_EVENT_VM_BIND_OP;
 	long ret = 0;
 
 	if (XE_IOCTL_DBG(xe, copy_from_user(&user_event, user_orig, sizeof(user_event))))
@@ -2269,6 +2269,320 @@ 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 xe_eudebug_queue_bind_event(struct xe_eudebug *d,
+				       struct xe_vm *vm,
+				       struct xe_eudebug_event *event)
+{
+	struct xe_eudebug_event_envelope *env;
+
+	lockdep_assert_held_write(&vm->lock);
+
+	env = kmalloc(sizeof(*env), GFP_KERNEL);
+	if (!env)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&env->link);
+	env->event = event;
+
+	spin_lock(&vm->eudebug.lock);
+	list_add_tail(&env->link, &vm->eudebug.events);
+
+	if (event->type == DRM_XE_EUDEBUG_EVENT_VM_BIND_OP)
+		++vm->eudebug.ops;
+	spin_unlock(&vm->eudebug.lock);
+
+	return 0;
+}
+
+static int queue_vm_bind_event(struct xe_eudebug *d,
+			       struct xe_vm *vm,
+			       u64 client_handle,
+			       u64 vm_handle,
+			       u32 bind_flags,
+			       u32 num_ops, u64 *seqno)
+{
+	struct xe_eudebug_event_vm_bind *e;
+	struct 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, GFP_KERNEL);
+	if (!event)
+		return -ENOMEM;
+
+	e = cast_event(e, event);
+	write_member(struct drm_xe_eudebug_event_vm_bind, e, client_handle, client_handle);
+	write_member(struct drm_xe_eudebug_event_vm_bind, e, vm_handle, vm_handle);
+	write_member(struct drm_xe_eudebug_event_vm_bind, e, flags, bind_flags);
+	write_member(struct drm_xe_eudebug_event_vm_bind, e, num_binds, num_ops);
+
+	/* If in discovery, no need to collect ops */
+	if (!completion_done(&d->discovery)) {
+		XE_WARN_ON(!num_ops);
+		return xe_eudebug_queue_event(d, event);
+	}
+
+	return xe_eudebug_queue_bind_event(d, vm, event);
+}
+
+static int vm_bind_event(struct xe_eudebug *d,
+			 struct xe_vm *vm,
+			 u32 num_ops,
+			 u64 *seqno)
+{
+	int h_c, h_vm;
+
+	h_c = find_handle(d->res, XE_EUDEBUG_RES_TYPE_CLIENT, vm->xef);
+	if (h_c < 0)
+		return h_c;
+
+	h_vm = find_handle(d->res, XE_EUDEBUG_RES_TYPE_VM, vm);
+	if (h_vm < 0)
+		return h_vm;
+
+	return queue_vm_bind_event(d, vm, h_c, h_vm, 0,
+				   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,
+			    u64 addr, u64 range,
+			    u64 *op_seqno)
+{
+	struct xe_eudebug_event_vm_bind_op *e;
+	struct 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,
+					*op_seqno, flags, sz, GFP_KERNEL);
+	if (!event)
+		return -ENOMEM;
+
+	e = cast_event(e, event);
+
+	write_member(struct drm_xe_eudebug_event_vm_bind_op, e, vm_bind_ref_seqno, bind_ref_seqno);
+	write_member(struct drm_xe_eudebug_event_vm_bind_op, e, num_extensions, num_extensions);
+	write_member(struct drm_xe_eudebug_event_vm_bind_op, e, addr, addr);
+	write_member(struct drm_xe_eudebug_event_vm_bind_op, e, range, range);
+
+	/* If in discovery, no need to collect ops */
+	if (!completion_done(&d->discovery))
+		return xe_eudebug_queue_event(d, event);
+
+	return xe_eudebug_queue_bind_event(d, vm, event);
+}
+
+static int vm_bind_op(struct xe_eudebug *d, struct xe_vm *vm,
+		      const u32 flags, const u64 bind_ref_seqno,
+		      u64 addr, u64 range)
+{
+	u64 op_seqno = 0;
+	u64 num_extensions = 0;
+	int ret;
+
+	ret = vm_bind_op_event(d, vm, flags, bind_ref_seqno, num_extensions,
+			       addr, range, &op_seqno);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+void xe_eudebug_vm_init(struct xe_vm *vm)
+{
+	INIT_LIST_HEAD(&vm->eudebug.events);
+	spin_lock_init(&vm->eudebug.lock);
+	vm->eudebug.ops = 0;
+	vm->eudebug.ref_seqno = 0;
+}
+
+void xe_eudebug_vm_bind_start(struct xe_vm *vm)
+{
+	struct xe_eudebug *d;
+	u64 seqno = 0;
+	int err;
+
+	if (!xe_vm_in_lr_mode(vm))
+		return;
+
+	d = xe_eudebug_get(vm->xef);
+	if (!d)
+		return;
+
+	lockdep_assert_held_write(&vm->lock);
+
+	if (XE_WARN_ON(!list_empty(&vm->eudebug.events)) ||
+	    XE_WARN_ON(vm->eudebug.ops) ||
+	    XE_WARN_ON(vm->eudebug.ref_seqno)) {
+		eu_err(d, "bind busy on %s",  __func__);
+		xe_eudebug_disconnect(d, -EINVAL);
+	}
+
+	err = vm_bind_event(d, vm, 0, &seqno);
+	if (err) {
+		eu_err(d, "error %d on %s", err, __func__);
+		xe_eudebug_disconnect(d, err);
+	}
+
+	spin_lock(&vm->eudebug.lock);
+	XE_WARN_ON(vm->eudebug.ref_seqno);
+	vm->eudebug.ref_seqno = seqno;
+	vm->eudebug.ops = 0;
+	spin_unlock(&vm->eudebug.lock);
+
+	xe_eudebug_put(d);
+}
+
+void xe_eudebug_vm_bind_op_add(struct xe_vm *vm, u32 op, u64 addr, u64 range)
+{
+	struct xe_eudebug *d;
+	u32 flags;
+
+	if (!xe_vm_in_lr_mode(vm))
+		return;
+
+	switch (op) {
+	case DRM_XE_VM_BIND_OP_MAP:
+	case DRM_XE_VM_BIND_OP_MAP_USERPTR:
+	{
+		flags = DRM_XE_EUDEBUG_EVENT_CREATE;
+		break;
+	}
+	case DRM_XE_VM_BIND_OP_UNMAP:
+	case DRM_XE_VM_BIND_OP_UNMAP_ALL:
+		flags = DRM_XE_EUDEBUG_EVENT_DESTROY;
+		break;
+	default:
+		flags = 0;
+		break;
+	}
+
+	if (!flags)
+		return;
+
+	d = xe_eudebug_get(vm->xef);
+	if (!d)
+		return;
+
+	xe_eudebug_event_put(d, vm_bind_op(d, vm, flags, 0, addr, range));
+}
+
+static struct xe_eudebug_event *fetch_bind_event(struct xe_vm * const vm)
+{
+	struct xe_eudebug_event_envelope *env;
+	struct xe_eudebug_event *e = NULL;
+
+	spin_lock(&vm->eudebug.lock);
+	env = list_first_entry_or_null(&vm->eudebug.events,
+				       struct xe_eudebug_event_envelope, link);
+	if (env) {
+		e = env->event;
+		list_del(&env->link);
+	}
+	spin_unlock(&vm->eudebug.lock);
+
+	kfree(env);
+
+	return e;
+}
+
+static void fill_vm_bind_fields(struct xe_vm *vm,
+				struct xe_eudebug_event *e,
+				bool ufence,
+				u32 bind_ops)
+{
+	struct xe_eudebug_event_vm_bind *eb = cast_event(eb, e);
+
+	eb->flags = ufence ?
+		DRM_XE_EUDEBUG_EVENT_VM_BIND_FLAG_UFENCE : 0;
+	eb->num_binds = bind_ops;
+}
+
+static void fill_vm_bind_op_fields(struct xe_vm *vm,
+				   struct xe_eudebug_event *e,
+				   u64 ref_seqno)
+{
+	struct xe_eudebug_event_vm_bind_op *op;
+
+	if (e->type != DRM_XE_EUDEBUG_EVENT_VM_BIND_OP)
+		return;
+
+	op = cast_event(op, e);
+	op->vm_bind_ref_seqno = ref_seqno;
+}
+
+void xe_eudebug_vm_bind_end(struct xe_vm *vm, bool has_ufence, int bind_err)
+{
+	struct xe_eudebug_event *e;
+	struct xe_eudebug *d;
+	u32 bind_ops;
+	u64 ref;
+
+	if (!xe_vm_in_lr_mode(vm))
+		return;
+
+	spin_lock(&vm->eudebug.lock);
+	ref = vm->eudebug.ref_seqno;
+	vm->eudebug.ref_seqno = 0;
+	bind_ops = vm->eudebug.ops;
+	vm->eudebug.ops = 0;
+	spin_unlock(&vm->eudebug.lock);
+
+	e = fetch_bind_event(vm);
+	if (!e)
+		return;
+
+	d = NULL;
+	if (!bind_err && ref) {
+		d = xe_eudebug_get(vm->xef);
+		if (d) {
+			if (bind_ops) {
+				fill_vm_bind_fields(vm, e, has_ufence, bind_ops);
+			} else {
+				/*
+				 * If there was no ops we are interested in,
+				 * we can omit the whole sequence
+				 */
+				xe_eudebug_put(d);
+				d = NULL;
+			}
+		}
+	}
+
+	while (e) {
+		int err = 0;
+
+		if (d) {
+			err = xe_eudebug_queue_event(d, e);
+			if (!err)
+				e = NULL;
+		}
+
+		if (err) {
+			xe_eudebug_disconnect(d, err);
+			xe_eudebug_put(d);
+			d = NULL;
+		}
+
+		kfree(e);
+
+		e = fetch_bind_event(vm);
+		if (e && ref)
+			fill_vm_bind_op_fields(vm, e, ref);
+	}
+
+	if (d)
+		xe_eudebug_put(d);
+}
+
 static int discover_client(struct xe_eudebug *d, struct xe_file *xef)
 {
 	struct xe_exec_queue *q;
diff --git a/drivers/gpu/drm/xe/xe_eudebug.h b/drivers/gpu/drm/xe/xe_eudebug.h
index 1fe86bec99e1..ccc7202b3308 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.h
+++ b/drivers/gpu/drm/xe/xe_eudebug.h
@@ -5,11 +5,14 @@
 
 #ifndef _XE_EUDEBUG_H_
 
+#include <linux/types.h>
+
 struct drm_device;
 struct drm_file;
 struct xe_device;
 struct xe_file;
 struct xe_vm;
+struct xe_vma;
 struct xe_exec_queue;
 struct xe_hw_engine;
 
@@ -33,6 +36,11 @@ void xe_eudebug_vm_destroy(struct xe_file *xef, struct xe_vm *vm);
 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_init(struct xe_vm *vm);
+void xe_eudebug_vm_bind_start(struct xe_vm *vm);
+void xe_eudebug_vm_bind_op_add(struct xe_vm *vm, u32 op, u64 addr, u64 range);
+void xe_eudebug_vm_bind_end(struct xe_vm *vm, bool has_ufence, int err);
+
 #else
 
 static inline int xe_eudebug_connect_ioctl(struct drm_device *dev,
@@ -53,6 +61,11 @@ 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_init(struct xe_vm *vm) { }
+static inline void xe_eudebug_vm_bind_start(struct xe_vm *vm) { }
+static inline void xe_eudebug_vm_bind_op_add(struct xe_vm *vm, u32 op, u64 addr, u64 range) { }
+static inline void xe_eudebug_vm_bind_end(struct xe_vm *vm, bool has_ufence, int err) { }
+
 #endif /* CONFIG_DRM_XE_EUDEBUG */
 
 #endif
diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
index 73b3f906da66..78739954967b 100644
--- a/drivers/gpu/drm/xe/xe_eudebug_types.h
+++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
@@ -177,6 +177,11 @@ struct xe_eudebug_event {
 	u8 data[];
 };
 
+struct xe_eudebug_event_envelope {
+	struct list_head link;
+	struct xe_eudebug_event *event;
+};
+
 /**
  * struct xe_eudebug_event_open - Internal event for client open/close
  */
@@ -258,4 +263,28 @@ struct xe_eudebug_event_eu_attention {
 	u8 bitmask[];
 };
 
+/**
+ * struct xe_eudebug_event_vm_bind - Internal event for vm bind/unbind operation
+ */
+struct xe_eudebug_event_vm_bind {
+	/** @base: base event */
+	struct xe_eudebug_event base;
+
+	u64 client_handle;
+	u64 vm_handle;
+
+	u32 flags;
+	u32 num_binds;
+};
+
+struct xe_eudebug_event_vm_bind_op {
+	/** @base: base event */
+	struct xe_eudebug_event base;
+	u64 vm_bind_ref_seqno;
+	u64 num_extensions;
+
+	u64 addr; /* Zero for unmap all ? */
+	u64 range; /* Zero for unmap all ? */
+};
+
 #endif
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index 9701a7efe257..eb10085bb455 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -1412,6 +1412,8 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags)
 	for_each_tile(tile, xe, id)
 		xe_range_fence_tree_init(&vm->rftree[id]);
 
+	xe_eudebug_vm_init(vm);
+
 	vm->pt_ops = &xelp_pt_ops;
 
 	/*
@@ -1640,6 +1642,8 @@ static void vm_destroy_work_func(struct work_struct *w)
 	struct xe_tile *tile;
 	u8 id;
 
+	xe_eudebug_vm_bind_end(vm, 0, -ENOENT);
+
 	/* xe_vm_close_and_put was not called? */
 	xe_assert(xe, !vm->size);
 
@@ -2649,7 +2653,7 @@ static void vm_bind_ioctl_ops_fini(struct xe_vm *vm, struct xe_vma_ops *vops,
 				   struct dma_fence *fence)
 {
 	struct xe_exec_queue *wait_exec_queue = to_wait_exec_queue(vm, vops->q);
-	struct xe_user_fence *ufence;
+	struct xe_user_fence *ufence = NULL;
 	struct xe_vma_op *op;
 	int i;
 
@@ -2664,6 +2668,9 @@ static void vm_bind_ioctl_ops_fini(struct xe_vm *vm, struct xe_vma_ops *vops,
 			xe_vma_destroy(gpuva_to_vma(op->base.remap.unmap->va),
 				       fence);
 	}
+
+	xe_eudebug_vm_bind_end(vm, ufence, 0);
+
 	if (ufence)
 		xe_sync_ufence_put(ufence);
 	for (i = 0; i < vops->num_syncs; i++)
@@ -3072,6 +3079,8 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
 		if (err)
 			goto unwind_ops;
 
+		xe_eudebug_vm_bind_op_add(vm, op, addr, range);
+
 #ifdef TEST_VM_OPS_ERROR
 		if (flags & FORCE_OP_ERROR) {
 			vops.inject_error = true;
@@ -3095,8 +3104,11 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
 	err = vm_bind_ioctl_ops_execute(vm, &vops);
 
 unwind_ops:
-	if (err && err != -ENODATA)
+	if (err && err != -ENODATA) {
+		xe_eudebug_vm_bind_end(vm, num_ufence > 0, err);
 		vm_bind_ioctl_ops_unwind(vm, ops, args->num_binds);
+	}
+
 	xe_vma_ops_fini(&vops);
 	for (i = args->num_binds - 1; i >= 0; --i)
 		if (ops[i])
diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_types.h
index 7f9a303e51d8..557b047ebdd7 100644
--- a/drivers/gpu/drm/xe/xe_vm_types.h
+++ b/drivers/gpu/drm/xe/xe_vm_types.h
@@ -282,6 +282,19 @@ struct xe_vm {
 	bool batch_invalidate_tlb;
 	/** @xef: XE file handle for tracking this VM's drm client */
 	struct xe_file *xef;
+
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+	struct {
+		/** @lock: Lock for eudebug_bind members */
+		spinlock_t lock;
+		/** @events: List of vm bind ops gathered */
+		struct list_head events;
+		/** @ops: How many operations we have stored */
+		u32 ops;
+		/** @ref_seqno: Reference to the VM_BIND that the ops relate */
+		u64 ref_seqno;
+	} eudebug;
+#endif
 };
 
 /** struct xe_vma_op_map - VMA map operation */
diff --git a/include/uapi/drm/xe_drm_eudebug.h b/include/uapi/drm/xe_drm_eudebug.h
index 3c43aefefedd..44cf1b699589 100644
--- a/include/uapi/drm/xe_drm_eudebug.h
+++ b/include/uapi/drm/xe_drm_eudebug.h
@@ -29,6 +29,8 @@ struct drm_xe_eudebug_event {
 #define DRM_XE_EUDEBUG_EVENT_VM			3
 #define DRM_XE_EUDEBUG_EVENT_EXEC_QUEUE		4
 #define DRM_XE_EUDEBUG_EVENT_EU_ATTENTION	5
+#define DRM_XE_EUDEBUG_EVENT_VM_BIND		6
+#define DRM_XE_EUDEBUG_EVENT_VM_BIND_OP		7
 
 	__u16 flags;
 #define DRM_XE_EUDEBUG_EVENT_CREATE		(1 << 0)
@@ -93,6 +95,68 @@ struct drm_xe_eudebug_eu_control {
 	__u64 bitmask_ptr;
 };
 
+/*
+ *  When client (debuggee) does vm_bind_ioctl() following event
+ *  sequence will be created (for the debugger):
+ *
+ *  ┌───────────────────────┐
+ *  │  EVENT_VM_BIND        ├───────┬─┬─┐
+ *  └───────────────────────┘       │ │ │
+ *      ┌───────────────────────┐   │ │ │
+ *      │ EVENT_VM_BIND_OP #1   ├───┘ │ │
+ *      └───────────────────────┘     │ │
+ *                 ...                │ │
+ *      ┌───────────────────────┐     │ │
+ *      │ EVENT_VM_BIND_OP #n   ├─────┘ │
+ *      └───────────────────────┘       │
+ *                                      │
+ *      ┌───────────────────────┐       │
+ *      │ EVENT_UFENCE          ├───────┘
+ *      └───────────────────────┘
+ *
+ * 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 {
+	struct drm_xe_eudebug_event base;
+
+	__u64 client_handle;
+	__u64 vm_handle;
+
+	__u32 flags;
+#define DRM_XE_EUDEBUG_EVENT_VM_BIND_FLAG_UFENCE (1 << 0)
+
+	__u32 num_binds;
+};
+
+struct drm_xe_eudebug_event_vm_bind_op {
+	struct drm_xe_eudebug_event base;
+	__u64 vm_bind_ref_seqno; /* *_event_vm_bind.base.seqno */
+	__u64 num_extensions;
+
+	__u64 addr; /* XXX: Zero for unmap all? */
+	__u64 range; /* XXX: Zero for unmap all? */
+};
+
 #if defined(__cplusplus)
 }
 #endif
-- 
2.34.1


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

* [PATCH 10/18] drm/xe/eudebug: Add UFENCE events with acks
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
                   ` (8 preceding siblings ...)
  2024-10-01 14:42 ` [PATCH 09/18] drm/xe/eudebug: Add vm bind and vm bind ops Mika Kuoppala
@ 2024-10-01 14:42 ` Mika Kuoppala
  2024-10-01 14:42 ` [PATCH 11/18] drm/xe/eudebug: vm open/pread/pwrite Mika Kuoppala
                   ` (10 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Mika Kuoppala @ 2024-10-01 14:42 UTC (permalink / raw)
  To: intel-xe; +Cc: Mika Kuoppala, Andrzej Hajda

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

Attach debugger in xe_user_fence, send UFENCE event
and stall normal user fence signal path to yield if
there is debugger attached to ufence.

When ack (ioctl) is received for the corresponding seqno,
signal ufence.

v2: - return err instead of 0 to guarantee signalling (Dominik)
    - checkpatch (Tilak)
    - Kconfig (Mika, Andrzej)
    - use lock instead of cmpxchg (Mika)

Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Signed-off-by: Andrzej Hajda <andrzej.hajda@intel.com>
---
 drivers/gpu/drm/xe/xe_eudebug.c       | 277 +++++++++++++++++++++++++-
 drivers/gpu/drm/xe/xe_eudebug.h       |  15 ++
 drivers/gpu/drm/xe/xe_eudebug_types.h |  13 ++
 drivers/gpu/drm/xe/xe_exec.c          |   2 +-
 drivers/gpu/drm/xe/xe_sync.c          |  45 +++--
 drivers/gpu/drm/xe/xe_sync.h          |   8 +-
 drivers/gpu/drm/xe/xe_sync_types.h    |  28 ++-
 drivers/gpu/drm/xe/xe_vm.c            |   4 +-
 include/uapi/drm/xe_drm_eudebug.h     |  13 ++
 9 files changed, 377 insertions(+), 28 deletions(-)

diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index 785658b2e199..68c33d2e8e77 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -32,6 +32,7 @@
 #include "xe_reg_sr.h"
 #include "xe_rtp.h"
 #include "xe_sched_job.h"
+#include "xe_sync.h"
 #include "xe_vm.h"
 #include "xe_wa.h"
 
@@ -239,11 +240,119 @@ static void xe_eudebug_free(struct kref *ref)
 	kfree_rcu(d, rcu);
 }
 
-static void xe_eudebug_put(struct xe_eudebug *d)
+void xe_eudebug_put(struct xe_eudebug *d)
 {
 	kref_put(&d->ref, xe_eudebug_free);
 }
 
+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);
+
+	if (READ_ONCE(ufence->signalled))
+		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.ordered_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;
+		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 struct task_struct *find_get_target(const pid_t nr)
 {
 	struct task_struct *task;
@@ -328,6 +437,8 @@ static bool xe_eudebug_detach(struct xe_device *xe,
 
 	eu_dbg(d, "session %lld detached with %d", d->session, err);
 
+	release_acks(d);
+
 	/* Our ref with the connection_link */
 	xe_eudebug_put(d);
 
@@ -453,7 +564,7 @@ _xe_eudebug_get(struct xe_file *xef)
 	return d;
 }
 
-static struct xe_eudebug *
+struct xe_eudebug *
 xe_eudebug_get(struct xe_file *xef)
 {
 	struct xe_eudebug *d;
@@ -824,7 +935,7 @@ static long xe_eudebug_read_event(struct xe_eudebug *d,
 		u64_to_user_ptr(arg);
 	struct drm_xe_eudebug_event user_event;
 	struct xe_eudebug_event *event;
-	const unsigned int max_event = DRM_XE_EUDEBUG_EVENT_VM_BIND_OP;
+	const unsigned int max_event = DRM_XE_EUDEBUG_EVENT_VM_BIND_UFENCE;
 	long ret = 0;
 
 	if (XE_IOCTL_DBG(xe, copy_from_user(&user_event, user_orig, sizeof(user_event))))
@@ -899,6 +1010,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_event 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;
+}
+
 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)
@@ -1090,7 +1239,10 @@ static long xe_eudebug_ioctl(struct file *file,
 		ret = xe_eudebug_eu_control(d, arg);
 		eu_dbg(d, "ioctl cmd=EU_CONTROL ret=%ld\n", ret);
 		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;
 	}
@@ -1764,6 +1916,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;
+
 	d->res = xe_eudebug_resources_alloc();
 	if (IS_ERR(d->res)) {
 		err = PTR_ERR(d->res);
@@ -2396,6 +2551,70 @@ static int vm_bind_op(struct xe_eudebug *d, struct xe_vm *vm,
 	return 0;
 }
 
+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(sizeof(*ack), GFP_KERNEL);
+	if (!ack)
+		return -ENOMEM;
+
+	ack->seqno = seqno;
+	ack->ts_insert = ktime_get();
+
+	spin_lock(&d->acks.lock);
+	old = rb_find_add(&ack->rb_node,
+			  &d->acks.tree, ack_insert_cmp);
+	if (!old) {
+		kref_get(&f->refcount);
+		ack->ufence = f;
+	}
+	spin_unlock(&d->acks.lock);
+
+	if (old) {
+		eu_dbg(d, "ACK: seqno=%llu: already exists", seqno);
+		kfree(ack);
+		return -EEXIST;
+	}
+
+	eu_dbg(d, "ACK: seqno=%llu: tracking started", seqno);
+
+	return 0;
+}
+
+static int vm_bind_ufence_event(struct xe_eudebug *d,
+				struct xe_user_fence *ufence)
+{
+	struct xe_eudebug_event *event;
+	struct 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;
+
+	seqno = atomic_long_inc_return(&d->events.seqno);
+
+	event = xe_eudebug_create_event(d, DRM_XE_EUDEBUG_EVENT_VM_BIND_UFENCE,
+					seqno, flags, sz, GFP_KERNEL);
+	if (!event)
+		return -ENOMEM;
+
+	e = cast_event(e, event);
+
+	write_member(struct drm_xe_eudebug_event_vm_bind_ufence,
+		     e, vm_bind_ref_seqno, ufence->eudebug.bind_ref_seqno);
+
+	ret = xe_eudebug_track_ufence(d, ufence, seqno);
+	if (!ret)
+		ret = xe_eudebug_queue_event(d, event);
+
+	return ret;
+}
+
 void xe_eudebug_vm_init(struct xe_vm *vm)
 {
 	INIT_LIST_HEAD(&vm->eudebug.events);
@@ -2583,6 +2802,24 @@ void xe_eudebug_vm_bind_end(struct xe_vm *vm, bool has_ufence, int bind_err)
 		xe_eudebug_put(d);
 }
 
+int xe_eudebug_vm_bind_ufence(struct xe_user_fence *ufence)
+{
+	struct xe_eudebug *d;
+	int err;
+
+	d = ufence->eudebug.debugger;
+	if (!d || xe_eudebug_detached(d))
+		return -ENOTCONN;
+
+	err = vm_bind_ufence_event(d, ufence);
+	if (err) {
+		eu_err(d, "error %d on %s", err, __func__);
+		xe_eudebug_disconnect(d, err);
+	}
+
+	return err;
+}
+
 static int discover_client(struct xe_eudebug *d, struct xe_file *xef)
 {
 	struct xe_exec_queue *q;
@@ -2675,3 +2912,35 @@ static void discovery_work_fn(struct work_struct *work)
 
 	xe_eudebug_put(d);
 }
+
+void xe_eudebug_ufence_init(struct xe_user_fence *ufence,
+			    struct xe_file *xef,
+			    struct xe_vm *vm)
+{
+	u64 bind_ref;
+
+	spin_lock(&vm->eudebug.lock);
+	bind_ref = vm->eudebug.ref_seqno;
+	spin_unlock(&vm->eudebug.lock);
+
+	spin_lock_init(&ufence->eudebug.lock);
+	INIT_WORK(&ufence->eudebug.worker, ufence_signal_worker);
+
+	ufence->eudebug.signalled_seqno = 0;
+
+	if (bind_ref) {
+		ufence->eudebug.debugger = xe_eudebug_get(xef);
+
+		if (ufence->eudebug.debugger)
+			ufence->eudebug.bind_ref_seqno = bind_ref;
+	}
+}
+
+void xe_eudebug_ufence_fini(struct xe_user_fence *ufence)
+{
+	if (!ufence->eudebug.debugger)
+		return;
+
+	xe_eudebug_put(ufence->eudebug.debugger);
+	ufence->eudebug.debugger = NULL;
+}
diff --git a/drivers/gpu/drm/xe/xe_eudebug.h b/drivers/gpu/drm/xe/xe_eudebug.h
index ccc7202b3308..ae720a1b05d9 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.h
+++ b/drivers/gpu/drm/xe/xe_eudebug.h
@@ -15,6 +15,7 @@ struct xe_vm;
 struct xe_vma;
 struct xe_exec_queue;
 struct xe_hw_engine;
+struct xe_user_fence;
 
 #if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
 
@@ -41,6 +42,13 @@ void xe_eudebug_vm_bind_start(struct xe_vm *vm);
 void xe_eudebug_vm_bind_op_add(struct xe_vm *vm, u32 op, u64 addr, u64 range);
 void xe_eudebug_vm_bind_end(struct xe_vm *vm, bool has_ufence, int err);
 
+int xe_eudebug_vm_bind_ufence(struct xe_user_fence *ufence);
+void xe_eudebug_ufence_init(struct xe_user_fence *ufence, struct xe_file *xef, struct xe_vm *vm);
+void xe_eudebug_ufence_fini(struct xe_user_fence *ufence);
+
+struct xe_eudebug *xe_eudebug_get(struct xe_file *xef);
+void xe_eudebug_put(struct xe_eudebug *d);
+
 #else
 
 static inline int xe_eudebug_connect_ioctl(struct drm_device *dev,
@@ -66,6 +74,13 @@ static inline void xe_eudebug_vm_bind_start(struct xe_vm *vm) { }
 static inline void xe_eudebug_vm_bind_op_add(struct xe_vm *vm, u32 op, u64 addr, u64 range) { }
 static inline void xe_eudebug_vm_bind_end(struct xe_vm *vm, bool has_ufence, int err) { }
 
+static inline int xe_eudebug_vm_bind_ufence(struct xe_user_fence *ufence) { return 0; }
+static inline void xe_eudebug_ufence_init(struct xe_user_fence *ufence, struct xe_file *xef, struct xe_vm *vm) { }
+static inline void xe_eudebug_ufence_fini(struct xe_user_fence *ufence) { }
+
+static inline struct xe_eudebug *xe_eudebug_get(struct xe_file *xef) { return NULL; }
+static inline void xe_eudebug_put(struct xe_eudebug *d) { }
+
 #endif /* CONFIG_DRM_XE_EUDEBUG */
 
 #endif
diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
index 78739954967b..aae7bc476c7e 100644
--- a/drivers/gpu/drm/xe/xe_eudebug_types.h
+++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
@@ -150,6 +150,14 @@ struct xe_eudebug {
 		atomic_long_t seqno;
 	} events;
 
+	/* user fences tracked by this debugger */
+	struct {
+		/** @lock: guards access to tree */
+		spinlock_t lock;
+
+		struct rb_root tree;
+	} acks;
+
 	/** @ops operations for eu_control */
 	struct xe_eudebug_eu_control_ops *ops;
 };
@@ -287,4 +295,9 @@ struct xe_eudebug_event_vm_bind_op {
 	u64 range; /* Zero for unmap all ? */
 };
 
+struct xe_eudebug_event_vm_bind_ufence {
+	struct xe_eudebug_event base;
+	u64 vm_bind_ref_seqno;
+};
+
 #endif
diff --git a/drivers/gpu/drm/xe/xe_exec.c b/drivers/gpu/drm/xe/xe_exec.c
index 7b38485817dc..052edd41dc4e 100644
--- a/drivers/gpu/drm/xe/xe_exec.c
+++ b/drivers/gpu/drm/xe/xe_exec.c
@@ -160,7 +160,7 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
 	vm = q->vm;
 
 	for (num_syncs = 0; num_syncs < args->num_syncs; num_syncs++) {
-		err = xe_sync_entry_parse(xe, xef, &syncs[num_syncs],
+		err = xe_sync_entry_parse(xe, xef, vm, &syncs[num_syncs],
 					  &syncs_user[num_syncs], SYNC_PARSE_FLAG_EXEC |
 					  (xe_vm_in_lr_mode(vm) ?
 					   SYNC_PARSE_FLAG_LR_MODE : 0));
diff --git a/drivers/gpu/drm/xe/xe_sync.c b/drivers/gpu/drm/xe/xe_sync.c
index bb3c2a830362..313437defc04 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_types.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,10 @@ 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,
+					       struct xe_file *xef,
+					       struct xe_vm *vm,
+					       u64 addr,
 					       u64 value)
 {
 	struct xe_user_fence *ufence;
@@ -58,7 +54,7 @@ static struct xe_user_fence *user_fence_create(struct xe_device *xe, u64 addr,
 	if (!access_ok(ptr, sizeof(*ptr)))
 		return ERR_PTR(-EFAULT);
 
-	ufence = kmalloc(sizeof(*ufence), GFP_KERNEL);
+	ufence = kzalloc(sizeof(*ufence), GFP_KERNEL);
 	if (!ufence)
 		return ERR_PTR(-ENOMEM);
 
@@ -69,12 +65,14 @@ 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, xef, vm);
+
 	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);
+	XE_WARN_ON(!ufence->signalled);
 
 	if (mmget_not_zero(ufence->mm)) {
 		kthread_use_mm(ufence->mm);
@@ -85,7 +83,20 @@ static void user_fence_worker(struct work_struct *w)
 	}
 
 	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);
+	int ret;
+
 	WRITE_ONCE(ufence->signalled, 1);
+
+	/* Lets see if debugger wants to track this */
+	ret = xe_eudebug_vm_bind_ufence(ufence);
+	if (ret)
+		xe_sync_ufence_signal(ufence);
+
 	user_fence_put(ufence);
 }
 
@@ -104,6 +115,7 @@ static void user_fence_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
 }
 
 int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef,
+			struct xe_vm *vm,
 			struct xe_sync_entry *sync,
 			struct drm_xe_sync __user *sync_user,
 			unsigned int flags)
@@ -185,7 +197,8 @@ int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef,
 		if (exec) {
 			sync->addr = sync_in.addr;
 		} else {
-			sync->ufence = user_fence_create(xe, sync_in.addr,
+			sync->ufence = user_fence_create(xe, xef, vm,
+							 sync_in.addr,
 							 sync_in.timeline_value);
 			if (XE_IOCTL_DBG(xe, IS_ERR(sync->ufence)))
 				return PTR_ERR(sync->ufence);
diff --git a/drivers/gpu/drm/xe/xe_sync.h b/drivers/gpu/drm/xe/xe_sync.h
index 256ffc1e54dc..f5bec2b1b4f6 100644
--- a/drivers/gpu/drm/xe/xe_sync.h
+++ b/drivers/gpu/drm/xe/xe_sync.h
@@ -9,8 +9,12 @@
 #include "xe_sync_types.h"
 
 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;
 
@@ -19,6 +23,7 @@ struct xe_vm;
 #define SYNC_PARSE_FLAG_DISALLOW_USER_FENCE	BIT(2)
 
 int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef,
+			struct xe_vm *vm,
 			struct xe_sync_entry *sync,
 			struct drm_xe_sync __user *sync_user,
 			unsigned int flags);
@@ -40,5 +45,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 30ac3f51993b..dcd3165e66a7 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/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index eb10085bb455..a836dfc5a86f 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -3031,9 +3031,11 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
 		}
 	}
 
+	xe_eudebug_vm_bind_start(vm);
+
 	syncs_user = u64_to_user_ptr(args->syncs);
 	for (num_syncs = 0; num_syncs < args->num_syncs; num_syncs++) {
-		err = xe_sync_entry_parse(xe, xef, &syncs[num_syncs],
+		err = xe_sync_entry_parse(xe, xef, vm, &syncs[num_syncs],
 					  &syncs_user[num_syncs],
 					  (xe_vm_in_lr_mode(vm) ?
 					   SYNC_PARSE_FLAG_LR_MODE : 0) |
diff --git a/include/uapi/drm/xe_drm_eudebug.h b/include/uapi/drm/xe_drm_eudebug.h
index 44cf1b699589..b2f6038ccc59 100644
--- a/include/uapi/drm/xe_drm_eudebug.h
+++ b/include/uapi/drm/xe_drm_eudebug.h
@@ -17,6 +17,7 @@ extern "C" {
  */
 #define DRM_XE_EUDEBUG_IOCTL_READ_EVENT		_IO('j', 0x0)
 #define DRM_XE_EUDEBUG_IOCTL_EU_CONTROL		_IOWR('j', 0x2, struct drm_xe_eudebug_eu_control)
+#define DRM_XE_EUDEBUG_IOCTL_ACK_EVENT		_IOW('j', 0x4, struct drm_xe_eudebug_ack_event)
 
 /* XXX: Document events to match their internal counterparts when moved to xe_drm.h */
 struct drm_xe_eudebug_event {
@@ -31,6 +32,7 @@ struct drm_xe_eudebug_event {
 #define DRM_XE_EUDEBUG_EVENT_EU_ATTENTION	5
 #define DRM_XE_EUDEBUG_EVENT_VM_BIND		6
 #define DRM_XE_EUDEBUG_EVENT_VM_BIND_OP		7
+#define DRM_XE_EUDEBUG_EVENT_VM_BIND_UFENCE	8
 
 	__u16 flags;
 #define DRM_XE_EUDEBUG_EVENT_CREATE		(1 << 0)
@@ -157,6 +159,17 @@ struct drm_xe_eudebug_event_vm_bind_op {
 	__u64 range; /* XXX: Zero for unmap all? */
 };
 
+struct drm_xe_eudebug_event_vm_bind_ufence {
+	struct drm_xe_eudebug_event base;
+	__u64 vm_bind_ref_seqno; /* *_event_vm_bind.base.seqno */
+};
+
+struct drm_xe_eudebug_ack_event {
+	__u32 type;
+	__u32 flags; /* MBZ */
+	__u64 seqno;
+};
+
 #if defined(__cplusplus)
 }
 #endif
-- 
2.34.1


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

* [PATCH 11/18] drm/xe/eudebug: vm open/pread/pwrite
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
                   ` (9 preceding siblings ...)
  2024-10-01 14:42 ` [PATCH 10/18] drm/xe/eudebug: Add UFENCE events with acks Mika Kuoppala
@ 2024-10-01 14:42 ` Mika Kuoppala
  2024-10-01 14:43 ` [PATCH 12/18] drm/xe/eudebug: implement userptr_vma access Mika Kuoppala
                   ` (9 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Mika Kuoppala @ 2024-10-01 14:42 UTC (permalink / raw)
  To: intel-xe; +Cc: Mika Kuoppala, Matthew Brost, Maciej Patelczyk

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)

Cc: Matthew Brost <matthew.brost@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Signed-off-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
---
 drivers/gpu/drm/xe/regs/xe_gt_regs.h |  24 ++
 drivers/gpu/drm/xe/xe_eudebug.c      | 480 +++++++++++++++++++++++++++
 include/uapi/drm/xe_drm_eudebug.h    |  18 +
 3 files changed, 522 insertions(+)

diff --git a/drivers/gpu/drm/xe/regs/xe_gt_regs.h b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
index a6225e4a8516..c304253090b2 100644
--- a/drivers/gpu/drm/xe/regs/xe_gt_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
@@ -549,6 +549,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 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)
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index 68c33d2e8e77..edad6d533d0b 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -5,9 +5,12 @@
 
 #include <linux/anon_inodes.h>
 #include <linux/delay.h>
+#include <linux/file.h>
 #include <linux/poll.h>
 #include <linux/uaccess.h>
+#include <linux/vmalloc.h>
 
+#include <drm/drm_drv.h>
 #include <drm/drm_managed.h>
 
 #include <generated/xe_wa_oob.h>
@@ -16,6 +19,7 @@
 #include "regs/xe_engine_regs.h"
 
 #include "xe_assert.h"
+#include "xe_bo.h"
 #include "xe_device.h"
 #include "xe_eudebug.h"
 #include "xe_eudebug_types.h"
@@ -1219,6 +1223,8 @@ static long xe_eudebug_eu_control(struct xe_eudebug *d, const u64 arg)
 	return ret;
 }
 
+static long xe_eudebug_vm_open_ioctl(struct xe_eudebug *d, unsigned long arg);
+
 static long xe_eudebug_ioctl(struct file *file,
 			     unsigned int cmd,
 			     unsigned long arg)
@@ -1243,6 +1249,11 @@ 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;
 	}
@@ -2944,3 +2955,472 @@ void xe_eudebug_ufence_fini(struct xe_user_fence *ufence)
 	xe_eudebug_put(ufence->eudebug.debugger);
 	ufence->eudebug.debugger = NULL;
 }
+
+static int xe_eudebug_bovma_access(struct xe_bo *bo, u64 offset,
+				    void *buf, u64 len, bool write)
+{
+	struct xe_device * const xe = xe_bo_device(bo);
+	int ret;
+	u32 flags_orig;
+
+	/* XXX: require pin?. Assert bo->vm held */
+	ret = xe_bo_lock(bo, true);
+	if (ret)
+		return ret;
+
+	/*
+	 * XXX: we want to use xe_bo_vmap but it insist that userspace
+	 * has provided it's need for CPU access. But that is the client
+	 * and we are the debugger. So we promote temporarily with
+	 * flag to allow xe_bo_vmap to work in our case even if the client
+	 * did not need cpu map.
+	 *
+	 * XXX: Fix this by adding extra flags to xe_bo_vmap?
+	 */
+	flags_orig = bo->flags;
+	bo->flags |= XE_BO_FLAG_NEEDS_CPU_ACCESS;
+
+	ret = xe_bo_vmap(bo);
+	if (!ret) {
+		if (write)
+			xe_map_memcpy_to(xe, &bo->vmap, offset, buf, len);
+		else
+			xe_map_memcpy_from(xe, buf, &bo->vmap, offset, len);
+
+		xe_bo_vunmap(bo);
+
+		ret = len;
+	}
+	bo->flags = flags_orig;
+
+	xe_bo_unlock(bo);
+
+	return ret;
+}
+
+static int xe_eudebug_vma_access(struct xe_vma *vma, u64 offset,
+				 void *buf, u64 len, bool write)
+{
+	struct xe_bo *bo;
+	u64 bytes;
+
+	lockdep_assert_held_write(&xe_vma_vm(vma)->lock);
+
+	if (XE_WARN_ON(offset >= xe_vma_size(vma)))
+		return -EINVAL;
+
+	bytes = min_t(u64, len, xe_vma_size(vma) - offset);
+	if (!bytes)
+		return 0;
+
+	bo = xe_bo_get(xe_vma_bo(vma));
+	if (bo) {
+		int ret;
+
+		ret = xe_eudebug_bovma_access(bo, offset, buf, bytes, write);
+		xe_bo_put(bo);
+
+		return ret;
+	}
+
+	return -EINVAL;
+}
+
+static int xe_eudebug_vm_access(struct xe_vm *vm, u64 offset,
+				void *buf, u64 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_file *xef;
+	struct xe_vm *vm;
+	u64 flags;
+	u64 client_id;
+	u64 vm_handle;
+	u64 timeout_ns;
+};
+
+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 struct xe_vm *find_vm_get(struct xe_eudebug *d, const u32 id)
+{
+	struct xe_vm *vm;
+
+	mutex_lock(&d->res->lock);
+	vm = find_resource__unlocked(d->res, XE_EUDEBUG_RES_TYPE_VM, id);
+	if (vm)
+		xe_vm_get(vm);
+
+	mutex_unlock(&d->res->lock);
+
+	return vm;
+}
+
+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 = find_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 client_handle=%llu, vm_handle=%llu, flags=0x%llx, pos=%llu, count=%zu\n",
+			write ? "write" : "read",
+			vmf->client_id, 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;
+	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;
+
+	ret = xe_force_wake_get(gt_to_fw(gt), hwe->domain);
+	if (ret)
+		return ret;
+
+	/* Prevent concurrent flushes */
+	mutex_lock(&d->eu_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));
+
+	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->eu_lock);
+	xe_force_wake_put(gt_to_fw(gt), hwe->domain);
+
+	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: client_handle=%llu, vm_handle=%llu, flags=0x%llx, start=%llu, end=%llu datasync=%d\n",
+	       vmf->client_id, 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? */
+		for_each_hw_engine(hwe, gt, id) {
+			u64 timeout_us;
+
+			if (hwe->class != XE_ENGINE_CLASS_RENDER &&
+			    hwe->class != XE_ENGINE_CLASS_COMPUTE)
+				continue;
+
+			timeout_us = div64_u64(vmf->timeout_ns, 1000ull);
+			ret = engine_rcu_flush(d, hwe, 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: client_handle=%llu, vm_handle=%llu, flags=0x%llx",
+	       vmf->client_id, vmf->vm_handle, vmf->flags);
+
+	xe_vm_put(vmf->vm);
+	xe_file_put(vmf->xef);
+	xe_eudebug_put(d);
+	drm_dev_put(&d->xe->drm);
+
+	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,
+};
+
+static 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_file *xef;
+	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;
+
+	xef = find_client_get(d, param.client_handle);
+	if (xef)
+		vm = find_vm_get(d, param.vm_handle);
+	else
+		vm = NULL;
+
+	if (XE_IOCTL_DBG(xe, !xef))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, !vm)) {
+		ret = -EINVAL;
+		goto out_file_put;
+	}
+
+	vmf = kzalloc(sizeof(*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->xef = xef;
+	vmf->flags = param.flags;
+	vmf->client_id = param.client_handle;
+	vmf->vm_handle = param.vm_handle;
+	vmf->timeout_ns = param.timeout_ns;
+
+	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;
+	}
+
+	file->f_mode |= FMODE_PREAD | FMODE_PWRITE |
+		FMODE_READ | FMODE_WRITE | FMODE_LSEEK;
+
+	fd_install(fd, file);
+
+	eu_dbg(d, "vm_open: client_handle=%llu, handle=%llu, flags=0x%llx, fd=%d",
+	       vmf->client_id, vmf->vm_handle, vmf->flags, fd);
+
+	XE_WARN_ON(ret);
+
+	drm_dev_get(&xe->drm);
+
+	return fd;
+
+out_fd_put:
+	put_unused_fd(fd);
+	xe_eudebug_put(d);
+out_free:
+	kfree(vmf);
+out_vm_put:
+	xe_vm_put(vm);
+out_file_put:
+	xe_file_put(xef);
+
+	XE_WARN_ON(ret >= 0);
+
+	return ret;
+}
diff --git a/include/uapi/drm/xe_drm_eudebug.h b/include/uapi/drm/xe_drm_eudebug.h
index b2f6038ccc59..9917280400d6 100644
--- a/include/uapi/drm/xe_drm_eudebug.h
+++ b/include/uapi/drm/xe_drm_eudebug.h
@@ -18,6 +18,7 @@ extern "C" {
 #define DRM_XE_EUDEBUG_IOCTL_READ_EVENT		_IO('j', 0x0)
 #define DRM_XE_EUDEBUG_IOCTL_EU_CONTROL		_IOWR('j', 0x2, struct drm_xe_eudebug_eu_control)
 #define DRM_XE_EUDEBUG_IOCTL_ACK_EVENT		_IOW('j', 0x4, struct drm_xe_eudebug_ack_event)
+#define DRM_XE_EUDEBUG_IOCTL_VM_OPEN		_IOW('j', 0x1, struct drm_xe_eudebug_vm_open)
 
 /* XXX: Document events to match their internal counterparts when moved to xe_drm.h */
 struct drm_xe_eudebug_event {
@@ -170,6 +171,23 @@ struct drm_xe_eudebug_ack_event {
 	__u64 seqno;
 };
 
+struct drm_xe_eudebug_vm_open {
+	/** @extensions: Pointer to the first extension struct, if any */
+	__u64 extensions;
+
+	/** @client_handle: id of client */
+	__u64 client_handle;
+
+	/** @vm_handle: id of vm */
+	__u64 vm_handle;
+
+	/** @flags: flags */
+	__u64 flags;
+
+	/** @timeout_ns: Timeout value in nanoseconds operations (fsync) */
+	__u64 timeout_ns;
+};
+
 #if defined(__cplusplus)
 }
 #endif
-- 
2.34.1


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

* [PATCH 12/18] drm/xe/eudebug: implement userptr_vma access
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
                   ` (10 preceding siblings ...)
  2024-10-01 14:42 ` [PATCH 11/18] drm/xe/eudebug: vm open/pread/pwrite Mika Kuoppala
@ 2024-10-01 14:43 ` Mika Kuoppala
  2024-10-05  3:48   ` Matthew Brost
                     ` (2 more replies)
  2024-10-01 14:43 ` [PATCH 13/18] drm/xe: Debug metadata create/destroy ioctls Mika Kuoppala
                   ` (8 subsequent siblings)
  20 siblings, 3 replies; 40+ messages in thread
From: Mika Kuoppala @ 2024-10-01 14:43 UTC (permalink / raw)
  To: intel-xe; +Cc: Andrzej Hajda, Maciej Patelczyk, Mika Kuoppala, Jonathan Cavitt

From: Andrzej Hajda <andrzej.hajda@intel.com>

Debugger needs to read/write program's vmas including userptr_vma.
Since hmm_range_fault is used to pin userptr vmas, it is possible
to map those vmas from debugger context.

v2: pin pages vs notifier, move to vm.c (Matthew)

Signed-off-by: Andrzej Hajda <andrzej.hajda@intel.com>
Signed-off-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Jonathan Cavitt <jonathan.cavitt@intel.com>
---
 drivers/gpu/drm/xe/xe_eudebug.c |  2 +-
 drivers/gpu/drm/xe/xe_vm.c      | 47 +++++++++++++++++++++++++++++++++
 drivers/gpu/drm/xe/xe_vm.h      |  3 +++
 3 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index edad6d533d0b..b09d7414cfe3 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -3023,7 +3023,7 @@ static int xe_eudebug_vma_access(struct xe_vma *vma, u64 offset,
 		return ret;
 	}
 
-	return -EINVAL;
+	return xe_uvma_access(to_userptr_vma(vma), offset, buf, bytes, write);
 }
 
 static int xe_eudebug_vm_access(struct xe_vm *vm, u64 offset,
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index a836dfc5a86f..5f891e76993b 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -3421,3 +3421,50 @@ void xe_vm_snapshot_free(struct xe_vm_snapshot *snap)
 	}
 	kvfree(snap);
 }
+
+int xe_uvma_access(struct xe_userptr_vma *uvma, u64 offset,
+		   void *buf, u64 len, bool write)
+{
+	struct xe_vm *vm = xe_vma_vm(&uvma->vma);
+	struct xe_userptr *up = &uvma->userptr;
+	struct xe_res_cursor cur = {};
+	int cur_len, ret = 0;
+
+	while (true) {
+		down_read(&vm->userptr.notifier_lock);
+		if (!xe_vma_userptr_check_repin(uvma))
+			break;
+
+		spin_lock(&vm->userptr.invalidated_lock);
+		list_del_init(&uvma->userptr.invalidate_link);
+		spin_unlock(&vm->userptr.invalidated_lock);
+
+		up_read(&vm->userptr.notifier_lock);
+		ret = xe_vma_userptr_pin_pages(uvma);
+		if (ret)
+			return ret;
+	}
+
+	if (!up->sg) {
+		ret = -EINVAL;
+		goto out_unlock_notifier;
+	}
+
+	for (xe_res_first_sg(up->sg, offset, len, &cur); cur.remaining;
+	     xe_res_next(&cur, cur_len)) {
+		void *ptr = kmap_local_page(sg_page(cur.sgl)) + cur.start;
+
+		cur_len = min(cur.size, cur.remaining);
+		if (write)
+			memcpy(ptr, buf, cur_len);
+		else
+			memcpy(buf, ptr, cur_len);
+		kunmap_local(ptr);
+		buf += cur_len;
+	}
+	ret = len;
+
+out_unlock_notifier:
+	up_read(&vm->userptr.notifier_lock);
+	return ret;
+}
diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h
index c864dba35e1d..99b9a9b011de 100644
--- a/drivers/gpu/drm/xe/xe_vm.h
+++ b/drivers/gpu/drm/xe/xe_vm.h
@@ -281,3 +281,6 @@ struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm *vm);
 void xe_vm_snapshot_capture_delayed(struct xe_vm_snapshot *snap);
 void xe_vm_snapshot_print(struct xe_vm_snapshot *snap, struct drm_printer *p);
 void xe_vm_snapshot_free(struct xe_vm_snapshot *snap);
+
+int xe_uvma_access(struct xe_userptr_vma *uvma, u64 offset,
+		   void *buf, u64 len, bool write);
-- 
2.34.1


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

* [PATCH 13/18] drm/xe: Debug metadata create/destroy ioctls
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
                   ` (11 preceding siblings ...)
  2024-10-01 14:43 ` [PATCH 12/18] drm/xe/eudebug: implement userptr_vma access Mika Kuoppala
@ 2024-10-01 14:43 ` Mika Kuoppala
  2024-10-01 16:25   ` Matthew Auld
  2024-10-01 14:43 ` [PATCH 14/18] drm/xe: Attach debug metadata to vma Mika Kuoppala
                   ` (7 subsequent siblings)
  20 siblings, 1 reply; 40+ messages in thread
From: Mika Kuoppala @ 2024-10-01 14:43 UTC (permalink / raw)
  To: intel-xe; +Cc: Dominik Grzegorzek, Mika Kuoppala

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

Ad a part of eu debug feature introduce debug metadata objects.
These are to be used to pass metadata between client and debugger,
by attaching them to vm_bind operations.

todo: WORK_IN_PROGRESS_* defines need to be reworded/refined when
      the real usage and need is established by l0+gdb.

v2: - include uapi/drm/xe_drm.h
    - metadata behind kconfig (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                  |   3 +-
 drivers/gpu/drm/xe/xe_debug_metadata.c       | 108 +++++++++++++++++++
 drivers/gpu/drm/xe/xe_debug_metadata.h       |  50 +++++++++
 drivers/gpu/drm/xe/xe_debug_metadata_types.h |  28 +++++
 drivers/gpu/drm/xe/xe_device.c               |   5 +
 drivers/gpu/drm/xe/xe_device.h               |   2 +
 drivers/gpu/drm/xe/xe_device_types.h         |   7 ++
 drivers/gpu/drm/xe/xe_eudebug.c              |  13 +++
 include/uapi/drm/xe_drm.h                    |  53 ++++++++-
 9 files changed, 267 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/xe/xe_debug_metadata.c
 create mode 100644 drivers/gpu/drm/xe/xe_debug_metadata.h
 create mode 100644 drivers/gpu/drm/xe/xe_debug_metadata_types.h

diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
index 660c1dbba8d0..a94f771529a4 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -114,7 +114,8 @@ xe-y += xe_bb.o \
 	xe_wa.o \
 	xe_wopcm.o
 
-xe-$(CONFIG_DRM_XE_EUDEBUG) += xe_eudebug.o
+xe-$(CONFIG_DRM_XE_EUDEBUG) += xe_eudebug.o \
+	xe_debug_metadata.o
 
 xe-$(CONFIG_HMM_MIRROR) += xe_hmm.o
 
diff --git a/drivers/gpu/drm/xe/xe_debug_metadata.c b/drivers/gpu/drm/xe/xe_debug_metadata.c
new file mode 100644
index 000000000000..72a00b628475
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_debug_metadata.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+#include "xe_debug_metadata.h"
+
+#include <drm/drm_device.h>
+#include <drm/drm_file.h>
+#include <uapi/drm/xe_drm.h>
+
+#include "xe_device.h"
+#include "xe_macros.h"
+
+static void xe_debug_metadata_release(struct kref *ref)
+{
+	struct xe_debug_metadata *mdata = container_of(ref, struct xe_debug_metadata, refcount);
+
+	kvfree(mdata->ptr);
+	kfree(mdata);
+}
+
+void xe_debug_metadata_put(struct xe_debug_metadata *mdata)
+{
+	kref_put(&mdata->refcount, xe_debug_metadata_release);
+}
+
+int xe_debug_metadata_create_ioctl(struct drm_device *dev,
+				   void *data,
+				   struct drm_file *file)
+{
+	struct xe_device *xe = to_xe_device(dev);
+	struct xe_file *xef = to_xe_file(file);
+	struct drm_xe_debug_metadata_create *args = data;
+	struct xe_debug_metadata *mdata;
+	int err;
+	u32 id;
+
+	if (XE_IOCTL_DBG(xe, args->extensions))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, args->type > DRM_XE_DEBUG_METADATA_PROGRAM_MODULE))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, !args->user_addr || !args->len))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, !access_ok(u64_to_user_ptr(args->user_addr), args->len)))
+		return -EFAULT;
+
+	mdata = kzalloc(sizeof(*mdata), GFP_KERNEL);
+	if (!mdata)
+		return -ENOMEM;
+
+	mdata->len = args->len;
+	mdata->type = args->type;
+
+	mdata->ptr = kvmalloc(mdata->len, GFP_KERNEL);
+	if (!mdata->ptr) {
+		kfree(mdata);
+		return -ENOMEM;
+	}
+	kref_init(&mdata->refcount);
+
+	err = copy_from_user(mdata->ptr, u64_to_user_ptr(args->user_addr), mdata->len);
+	if (err) {
+		err = -EFAULT;
+		goto put_mdata;
+	}
+
+	mutex_lock(&xef->eudebug.metadata.lock);
+	err = xa_alloc(&xef->eudebug.metadata.xa, &id, mdata, xa_limit_32b, GFP_KERNEL);
+	mutex_unlock(&xef->eudebug.metadata.lock);
+
+	args->metadata_id = id;
+	mdata->id = id;
+
+	if (err)
+		goto put_mdata;
+
+	return 0;
+
+put_mdata:
+	xe_debug_metadata_put(mdata);
+	return err;
+}
+
+int xe_debug_metadata_destroy_ioctl(struct drm_device *dev,
+				    void *data,
+				    struct drm_file *file)
+{
+	struct xe_device *xe = to_xe_device(dev);
+	struct xe_file *xef = to_xe_file(file);
+	struct drm_xe_debug_metadata_destroy * const args = data;
+	struct xe_debug_metadata *mdata;
+
+	if (XE_IOCTL_DBG(xe, args->extensions))
+		return -EINVAL;
+
+	mutex_lock(&xef->eudebug.metadata.lock);
+	mdata = xa_erase(&xef->eudebug.metadata.xa, args->metadata_id);
+	mutex_unlock(&xef->eudebug.metadata.lock);
+	if (XE_IOCTL_DBG(xe, !mdata))
+		return -ENOENT;
+
+	xe_debug_metadata_put(mdata);
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/xe/xe_debug_metadata.h b/drivers/gpu/drm/xe/xe_debug_metadata.h
new file mode 100644
index 000000000000..3266c25e657e
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_debug_metadata.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#ifndef _XE_DEBUG_METADATA_H_
+#define _XE_DEBUG_METADATA_H_
+
+struct drm_device;
+struct drm_file;
+
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+
+#include "xe_debug_metadata_types.h"
+
+void xe_debug_metadata_put(struct xe_debug_metadata *mdata);
+
+int xe_debug_metadata_create_ioctl(struct drm_device *dev,
+				   void *data,
+				   struct drm_file *file);
+
+int xe_debug_metadata_destroy_ioctl(struct drm_device *dev,
+				    void *data,
+				    struct drm_file *file);
+#else /* CONFIG_DRM_XE_EUDEBUG */
+
+#include <linux/errno.h>
+
+struct xe_debug_metadata;
+
+static inline void xe_debug_metadata_put(struct xe_debug_metadata *mdata) { }
+
+static inline int xe_debug_metadata_create_ioctl(struct drm_device *dev,
+						 void *data,
+						 struct drm_file *file)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int xe_debug_metadata_destroy_ioctl(struct drm_device *dev,
+						  void *data,
+						  struct drm_file *file)
+{
+	return -EOPNOTSUPP;
+}
+
+#endif /* CONFIG_DRM_XE_EUDEBUG */
+
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_debug_metadata_types.h b/drivers/gpu/drm/xe/xe_debug_metadata_types.h
new file mode 100644
index 000000000000..508f2fdbbc42
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_debug_metadata_types.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#ifndef _XE_DEBUG_METADATA_TYPES_H_
+#define _XE_DEBUG_METADATA_TYPES_H_
+
+#include <linux/kref.h>
+
+struct xe_debug_metadata {
+	/** @type: type of given metadata */
+	u64 type;
+
+	/** @ptr: copy of userptr, given as a metadata payload */
+	void *ptr;
+
+	/** @len: length, in bytes of the metadata */
+	u64 len;
+
+	/** @id */
+	u64 id;
+
+	/** @ref: reference count */
+	struct kref refcount;
+};
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index 77049c56c26c..e0d55e11ace6 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -24,6 +24,7 @@
 #include "xe_bo.h"
 #include "xe_debugfs.h"
 #include "xe_devcoredump.h"
+#include "xe_debug_metadata.h"
 #include "xe_dma_buf.h"
 #include "xe_drm_client.h"
 #include "xe_drv.h"
@@ -203,6 +204,10 @@ static const struct drm_ioctl_desc xe_ioctls[] = {
 			  DRM_RENDER_ALLOW),
 	DRM_IOCTL_DEF_DRV(XE_OBSERVATION, xe_observation_ioctl, DRM_RENDER_ALLOW),
 	DRM_IOCTL_DEF_DRV(XE_EUDEBUG_CONNECT, xe_eudebug_connect_ioctl, DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(XE_DEBUG_METADATA_CREATE, xe_debug_metadata_create_ioctl,
+			  DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(XE_DEBUG_METADATA_DESTROY, xe_debug_metadata_destroy_ioctl,
+			  DRM_RENDER_ALLOW),
 };
 
 static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
diff --git a/drivers/gpu/drm/xe/xe_device.h b/drivers/gpu/drm/xe/xe_device.h
index b2fc85b1d26e..ce01e3fc9fdf 100644
--- a/drivers/gpu/drm/xe/xe_device.h
+++ b/drivers/gpu/drm/xe/xe_device.h
@@ -204,6 +204,8 @@ static inline int xe_eudebug_needs_lock(const unsigned int cmd)
 	case DRM_XE_EXEC_QUEUE_CREATE:
 	case DRM_XE_EXEC_QUEUE_DESTROY:
 	case DRM_XE_EUDEBUG_CONNECT:
+	case DRM_XE_DEBUG_METADATA_CREATE:
+	case DRM_XE_DEBUG_METADATA_DESTROY:
 		return 1;
 	}
 
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index 562cc8930533..40b0f61b64b8 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -671,6 +671,13 @@ struct xe_file {
 	struct {
 		/** @client_link: list entry in xe_device.clients.list */
 		struct list_head client_link;
+
+		struct {
+			/** @xa: xarray to store debug metadata */
+			struct xarray xa;
+			/** @lock: protects debug metadata xarray */
+			struct mutex lock;
+		} metadata;
 	} eudebug;
 #endif
 };
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index b09d7414cfe3..6ee06a58b640 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -21,6 +21,7 @@
 #include "xe_assert.h"
 #include "xe_bo.h"
 #include "xe_device.h"
+#include "xe_debug_metadata.h"
 #include "xe_eudebug.h"
 #include "xe_eudebug_types.h"
 #include "xe_exec_queue.h"
@@ -2132,6 +2133,8 @@ void xe_eudebug_file_open(struct xe_file *xef)
 	struct xe_eudebug *d;
 
 	INIT_LIST_HEAD(&xef->eudebug.client_link);
+	mutex_init(&xef->eudebug.metadata.lock);
+	xa_init_flags(&xef->eudebug.metadata.xa, XA_FLAGS_ALLOC1);
 
 	down_read(&xef->xe->eudebug.discovery_lock);
 
@@ -2149,12 +2152,22 @@ void xe_eudebug_file_open(struct xe_file *xef)
 void xe_eudebug_file_close(struct xe_file *xef)
 {
 	struct xe_eudebug *d;
+	unsigned long idx;
+	struct xe_debug_metadata *mdata;
 
 	down_read(&xef->xe->eudebug.discovery_lock);
 	d = xe_eudebug_get(xef);
 	if (d)
 		xe_eudebug_event_put(d, client_destroy_event(d, xef));
 
+	mutex_lock(&xef->eudebug.metadata.lock);
+	xa_for_each(&xef->eudebug.metadata.xa, idx, mdata)
+		xe_debug_metadata_put(mdata);
+	mutex_unlock(&xef->eudebug.metadata.lock);
+
+	xa_destroy(&xef->eudebug.metadata.xa);
+	mutex_destroy(&xef->eudebug.metadata.lock);
+
 	spin_lock(&xef->xe->clients.lock);
 	list_del_init(&xef->eudebug.client_link);
 	spin_unlock(&xef->xe->clients.lock);
diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h
index 985dcf852458..0d75a072b838 100644
--- a/include/uapi/drm/xe_drm.h
+++ b/include/uapi/drm/xe_drm.h
@@ -103,7 +103,8 @@ extern "C" {
 #define DRM_XE_WAIT_USER_FENCE		0x0a
 #define DRM_XE_OBSERVATION		0x0b
 #define DRM_XE_EUDEBUG_CONNECT		0x0c
-
+#define DRM_XE_DEBUG_METADATA_CREATE	0x0d
+#define DRM_XE_DEBUG_METADATA_DESTROY	0x0e
 /* Must be kept compact -- no holes */
 
 #define DRM_IOCTL_XE_DEVICE_QUERY		DRM_IOWR(DRM_COMMAND_BASE + DRM_XE_DEVICE_QUERY, struct drm_xe_device_query)
@@ -119,6 +120,8 @@ extern "C" {
 #define DRM_IOCTL_XE_WAIT_USER_FENCE		DRM_IOWR(DRM_COMMAND_BASE + DRM_XE_WAIT_USER_FENCE, struct drm_xe_wait_user_fence)
 #define DRM_IOCTL_XE_OBSERVATION		DRM_IOW(DRM_COMMAND_BASE + DRM_XE_OBSERVATION, struct drm_xe_observation_param)
 #define DRM_IOCTL_XE_EUDEBUG_CONNECT		DRM_IOWR(DRM_COMMAND_BASE + DRM_XE_EUDEBUG_CONNECT, struct drm_xe_eudebug_connect)
+#define DRM_IOCTL_XE_DEBUG_METADATA_CREATE	 DRM_IOWR(DRM_COMMAND_BASE + DRM_XE_DEBUG_METADATA_CREATE, struct drm_xe_debug_metadata_create)
+#define DRM_IOCTL_XE_DEBUG_METADATA_DESTROY	 DRM_IOW(DRM_COMMAND_BASE + DRM_XE_DEBUG_METADATA_DESTROY, struct drm_xe_debug_metadata_destroy)
 
 /**
  * DOC: Xe IOCTL Extensions
@@ -1714,6 +1717,54 @@ struct drm_xe_eudebug_connect {
 	__u32 version; /* output: current ABI (ioctl / events) version */
 };
 
+/*
+ * struct drm_xe_debug_metadata_create - Create debug metadata
+ *
+ * Add a region of user memory to be marked as debug metadata.
+ * When the debugger attaches, the metadata regions will be delivered
+ * for debugger. Debugger can then map these regions to help decode
+ * the program state.
+ *
+ * Returns handle to created metadata entry.
+ */
+struct drm_xe_debug_metadata_create {
+	/** @extensions: Pointer to the first extension struct, if any */
+	__u64 extensions;
+
+#define DRM_XE_DEBUG_METADATA_ELF_BINARY     0
+#define DRM_XE_DEBUG_METADATA_PROGRAM_MODULE 1
+#define WORK_IN_PROGRESS_DRM_XE_DEBUG_METADATA_MODULE_AREA 2
+#define WORK_IN_PROGRESS_DRM_XE_DEBUG_METADATA_SBA_AREA 3
+#define WORK_IN_PROGRESS_DRM_XE_DEBUG_METADATA_SIP_AREA 4
+#define WORK_IN_PROGRESS_DRM_XE_DEBUG_METADATA_NUM (1 + \
+	  WORK_IN_PROGRESS_DRM_XE_DEBUG_METADATA_SIP_AREA)
+
+	/** @type: Type of metadata */
+	__u64 type;
+
+	/** @user_addr: pointer to start of the metadata */
+	__u64 user_addr;
+
+	/** @len: length, in bytes of the medata */
+	__u64 len;
+
+	/** @metadata_id: created metadata handle (out) */
+	__u32 metadata_id;
+};
+
+/**
+ * struct drm_xe_debug_metadata_destroy - Destroy debug metadata
+ *
+ * Destroy debug metadata.
+ */
+struct drm_xe_debug_metadata_destroy {
+	/** @extensions: Pointer to the first extension struct, if any */
+	__u64 extensions;
+
+	/** @metadata_id: metadata handle to destroy */
+	__u32 metadata_id;
+};
+
 #include "xe_drm_eudebug.h"
 
 #if defined(__cplusplus)
-- 
2.34.1


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

* [PATCH 14/18] drm/xe: Attach debug metadata to vma
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
                   ` (12 preceding siblings ...)
  2024-10-01 14:43 ` [PATCH 13/18] drm/xe: Debug metadata create/destroy ioctls Mika Kuoppala
@ 2024-10-01 14:43 ` Mika Kuoppala
  2024-10-01 14:43 ` [PATCH 15/18] drm/xe/eudebug: Add debug metadata support for xe_eudebug Mika Kuoppala
                   ` (6 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Mika Kuoppala @ 2024-10-01 14:43 UTC (permalink / raw)
  To: intel-xe; +Cc: Dominik Grzegorzek, Maciej Patelczyk, Mika Kuoppala

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

Introduces a vm_bind_op extension, enabling users to attach metadata objects
to each [OP_MAP|OP_MAP_USERPTR] operation. This interface will be utilized
by the EU debugger to relay information about the contents of specified
VMAs from the debugee to the debugger process.

v2: move vma metadata handling behind Kconfig (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>
---
 drivers/gpu/drm/xe/xe_debug_metadata.c | 120 +++++++++++++++++++++++++
 drivers/gpu/drm/xe/xe_debug_metadata.h |  48 ++++++++++
 drivers/gpu/drm/xe/xe_vm.c             |  99 +++++++++++++++++++-
 drivers/gpu/drm/xe/xe_vm_types.h       |  27 ++++++
 include/uapi/drm/xe_drm.h              |  19 ++++
 5 files changed, 309 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/xe/xe_debug_metadata.c b/drivers/gpu/drm/xe/xe_debug_metadata.c
index 72a00b628475..303640f8ba1f 100644
--- a/drivers/gpu/drm/xe/xe_debug_metadata.c
+++ b/drivers/gpu/drm/xe/xe_debug_metadata.c
@@ -10,6 +10,113 @@
 
 #include "xe_device.h"
 #include "xe_macros.h"
+#include "xe_vm.h"
+
+void xe_eudebug_free_vma_metadata(struct xe_eudebug_vma_metadata *mdata)
+{
+	struct xe_vma_debug_metadata *vmad, *tmp;
+
+	list_for_each_entry_safe(vmad, tmp, &mdata->list, link) {
+		list_del(&vmad->link);
+		kfree(vmad);
+	}
+}
+
+static struct xe_vma_debug_metadata *
+vma_new_debug_metadata(u32 metadata_id, u64 cookie)
+{
+	struct xe_vma_debug_metadata *vmad;
+
+	vmad = kzalloc(sizeof(*vmad), GFP_KERNEL);
+	if (!vmad)
+		return ERR_PTR(-ENOMEM);
+
+	INIT_LIST_HEAD(&vmad->link);
+
+	vmad->metadata_id = metadata_id;
+	vmad->cookie = cookie;
+
+	return vmad;
+}
+
+int xe_eudebug_copy_vma_metadata(struct xe_eudebug_vma_metadata *from,
+				 struct xe_eudebug_vma_metadata *to)
+{
+	struct xe_vma_debug_metadata *vmad, *vma;
+
+	list_for_each_entry(vmad, &from->list, link) {
+		vma = vma_new_debug_metadata(vmad->metadata_id, vmad->cookie);
+		if (IS_ERR(vma))
+			return PTR_ERR(vma);
+
+		list_add_tail(&vmad->link, &to->list);
+	}
+
+	return 0;
+}
+
+static int vma_new_debug_metadata_op(struct xe_vma_op *op,
+				     u32 metadata_id, u64 cookie,
+				     u64 flags)
+{
+	struct xe_vma_debug_metadata *vmad;
+
+	vmad = vma_new_debug_metadata(metadata_id, cookie);
+	if (IS_ERR(vmad))
+		return PTR_ERR(vmad);
+
+	list_add_tail(&vmad->link, &op->map.eudebug.metadata.list);
+
+	return 0;
+}
+
+int vm_bind_op_ext_attach_debug(struct xe_device *xe,
+				struct xe_file *xef,
+				struct drm_gpuva_ops *ops,
+				u32 operation, u64 extension)
+{
+	u64 __user *address = u64_to_user_ptr(extension);
+	struct drm_xe_vm_bind_op_ext_attach_debug ext;
+	struct xe_debug_metadata *mdata;
+	struct drm_gpuva_op *__op;
+	int err;
+
+	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_MAP_USERPTR &&
+			 operation != DRM_XE_VM_BIND_OP_MAP))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, ext.flags))
+		return -EINVAL;
+
+	mdata = xe_debug_metadata_get(xef, (u32)ext.metadata_id);
+	if (XE_IOCTL_DBG(xe, !mdata))
+		return -ENOENT;
+
+	/* care about metadata existence only on the time of attach */
+	xe_debug_metadata_put(mdata);
+
+	if (!ops)
+		return 0;
+
+	drm_gpuva_for_each_op(__op, ops) {
+		struct xe_vma_op *op = gpuva_op_to_vma_op(__op);
+
+		if (op->base.op == DRM_GPUVA_OP_MAP) {
+			err = vma_new_debug_metadata_op(op,
+							ext.metadata_id,
+							ext.cookie,
+							ext.flags);
+			if (err)
+				return err;
+		}
+	}
+	return 0;
+}
 
 static void xe_debug_metadata_release(struct kref *ref)
 {
@@ -24,6 +131,19 @@ void xe_debug_metadata_put(struct xe_debug_metadata *mdata)
 	kref_put(&mdata->refcount, xe_debug_metadata_release);
 }
 
+struct xe_debug_metadata *xe_debug_metadata_get(struct xe_file *xef, u32 id)
+{
+	struct xe_debug_metadata *mdata;
+
+	mutex_lock(&xef->eudebug.metadata.lock);
+	mdata = xa_load(&xef->eudebug.metadata.xa, id);
+	if (mdata)
+		kref_get(&mdata->refcount);
+	mutex_unlock(&xef->eudebug.metadata.lock);
+
+	return mdata;
+}
+
 int xe_debug_metadata_create_ioctl(struct drm_device *dev,
 				   void *data,
 				   struct drm_file *file)
diff --git a/drivers/gpu/drm/xe/xe_debug_metadata.h b/drivers/gpu/drm/xe/xe_debug_metadata.h
index 3266c25e657e..13b763ee06e1 100644
--- a/drivers/gpu/drm/xe/xe_debug_metadata.h
+++ b/drivers/gpu/drm/xe/xe_debug_metadata.h
@@ -6,13 +6,18 @@
 #ifndef _XE_DEBUG_METADATA_H_
 #define _XE_DEBUG_METADATA_H_
 
+#include <linux/types.h>
+
 struct drm_device;
 struct drm_file;
+struct xe_file;
 
 #if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
 
 #include "xe_debug_metadata_types.h"
+#include "xe_vm_types.h"
 
+struct xe_debug_metadata *xe_debug_metadata_get(struct xe_file *xef, u32 id);
 void xe_debug_metadata_put(struct xe_debug_metadata *mdata);
 
 int xe_debug_metadata_create_ioctl(struct drm_device *dev,
@@ -22,12 +27,32 @@ int xe_debug_metadata_create_ioctl(struct drm_device *dev,
 int xe_debug_metadata_destroy_ioctl(struct drm_device *dev,
 				    void *data,
 				    struct drm_file *file);
+
+static inline void xe_eudebug_move_vma_metadata(struct xe_eudebug_vma_metadata *from,
+						struct xe_eudebug_vma_metadata *to)
+{
+	list_splice_tail_init(&from->list, &to->list);
+}
+
+int xe_eudebug_copy_vma_metadata(struct xe_eudebug_vma_metadata *from,
+				 struct xe_eudebug_vma_metadata *to);
+void xe_eudebug_free_vma_metadata(struct xe_eudebug_vma_metadata *mdata);
+
+int vm_bind_op_ext_attach_debug(struct xe_device *xe,
+				struct xe_file *xef,
+				struct drm_gpuva_ops *ops,
+				u32 operation, u64 extension);
+
 #else /* CONFIG_DRM_XE_EUDEBUG */
 
 #include <linux/errno.h>
 
 struct xe_debug_metadata;
+struct xe_device;
+struct xe_eudebug_vma_metadata;
+struct drm_gpuva_ops;
 
+static inline struct xe_debug_metadata *xe_debug_metadata_get(struct xe_file *xef, u32 id) { return NULL; }
 static inline void xe_debug_metadata_put(struct xe_debug_metadata *mdata) { }
 
 static inline int xe_debug_metadata_create_ioctl(struct drm_device *dev,
@@ -44,6 +69,29 @@ static inline int xe_debug_metadata_destroy_ioctl(struct drm_device *dev,
 	return -EOPNOTSUPP;
 }
 
+static inline void xe_eudebug_move_vma_metadata(struct xe_eudebug_vma_metadata *from,
+						struct xe_eudebug_vma_metadata *to)
+{
+}
+
+static inline int xe_eudebug_copy_vma_metadata(struct xe_eudebug_vma_metadata *from,
+					       struct xe_eudebug_vma_metadata *to)
+{
+	return 0;
+}
+
+static inline void xe_eudebug_free_vma_metadata(struct xe_eudebug_vma_metadata *mdata)
+{
+}
+
+static inline int vm_bind_op_ext_attach_debug(struct xe_device *xe,
+					      struct xe_file *xef,
+					      struct drm_gpuva_ops *ops,
+					      u32 operation, u64 extension)
+{
+	return -EINVAL;
+}
+
 #endif /* CONFIG_DRM_XE_EUDEBUG */
 
 
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index 5f891e76993b..8c48223892c6 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_metadata.h"
 #include "xe_device.h"
 #include "xe_drm_client.h"
 #include "xe_eudebug.h"
@@ -944,6 +945,9 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm,
 			vma->gpuva.gem.obj = &bo->ttm.base;
 	}
 
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+	INIT_LIST_HEAD(&vma->eudebug.metadata.list);
+#endif
 	INIT_LIST_HEAD(&vma->combined_links.rebind);
 
 	INIT_LIST_HEAD(&vma->gpuva.gem.entry);
@@ -1036,6 +1040,7 @@ static void xe_vma_destroy_late(struct xe_vma *vma)
 		xe_bo_put(xe_vma_bo(vma));
 	}
 
+	xe_eudebug_free_vma_metadata(&vma->eudebug.metadata);
 	xe_vma_free(vma);
 }
 
@@ -1978,6 +1983,9 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_bo *bo,
 			op->map.is_null = flags & DRM_XE_VM_BIND_FLAG_NULL;
 			op->map.dumpable = flags & DRM_XE_VM_BIND_FLAG_DUMPABLE;
 			op->map.pat_index = pat_index;
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+			INIT_LIST_HEAD(&op->map.eudebug.metadata.list);
+#endif
 		} else if (__op->op == DRM_GPUVA_OP_PREFETCH) {
 			op->prefetch.region = prefetch_region;
 		}
@@ -2168,11 +2176,13 @@ static int vm_bind_ioctl_ops_parse(struct xe_vm *vm, struct drm_gpuva_ops *ops,
 			flags |= op->map.dumpable ?
 				VMA_CREATE_FLAG_DUMPABLE : 0;
 
-			vma = new_vma(vm, &op->base.map, op->map.pat_index,
-				      flags);
+			vma = new_vma(vm, &op->base.map, op->map.pat_index, flags);
 			if (IS_ERR(vma))
 				return PTR_ERR(vma);
 
+			xe_eudebug_move_vma_metadata(&op->map.eudebug.metadata,
+						     &vma->eudebug.metadata);
+
 			op->map.vma = vma;
 			if (op->map.immediate || !xe_vm_in_fault_mode(vm))
 				xe_vma_ops_incr_pt_update_ops(vops,
@@ -2203,6 +2213,9 @@ static int vm_bind_ioctl_ops_parse(struct xe_vm *vm, struct drm_gpuva_ops *ops,
 				if (IS_ERR(vma))
 					return PTR_ERR(vma);
 
+				xe_eudebug_move_vma_metadata(&old->eudebug.metadata,
+							     &vma->eudebug.metadata);
+
 				op->remap.prev = vma;
 
 				/*
@@ -2242,6 +2255,16 @@ static int vm_bind_ioctl_ops_parse(struct xe_vm *vm, struct drm_gpuva_ops *ops,
 				if (IS_ERR(vma))
 					return PTR_ERR(vma);
 
+				if (op->base.remap.prev) {
+					err = xe_eudebug_copy_vma_metadata(&op->remap.prev->eudebug.metadata,
+									   &vma->eudebug.metadata);
+					if (err)
+						return err;
+				} else {
+					xe_eudebug_move_vma_metadata(&old->eudebug.metadata,
+								     &vma->eudebug.metadata);
+				}
+
 				op->remap.next = vma;
 
 				/*
@@ -2292,6 +2315,7 @@ static void xe_vma_op_unwind(struct xe_vm *vm, struct xe_vma_op *op,
 	switch (op->base.op) {
 	case DRM_GPUVA_OP_MAP:
 		if (op->map.vma) {
+			xe_eudebug_free_vma_metadata(&op->map.eudebug.metadata);
 			prep_vma_destroy(vm, op->map.vma, post_commit);
 			xe_vma_destroy_unlocked(op->map.vma);
 		}
@@ -2530,6 +2554,58 @@ static int vm_ops_setup_tile_args(struct xe_vm *vm, struct xe_vma_ops *vops)
 	}
 
 	return number_tiles;
+};
+
+typedef int (*xe_vm_bind_op_user_extension_fn)(struct xe_device *xe,
+					       struct xe_file *xef,
+					       struct drm_gpuva_ops *ops,
+					       u32 operation, u64 extension);
+
+static const xe_vm_bind_op_user_extension_fn vm_bind_op_extension_funcs[] = {
+	[XE_VM_BIND_OP_EXTENSIONS_ATTACH_DEBUG] = vm_bind_op_ext_attach_debug,
+};
+
+#define MAX_USER_EXTENSIONS	16
+static int vm_bind_op_user_extensions(struct xe_device *xe,
+				      struct xe_file *xef,
+				      struct drm_gpuva_ops *ops,
+				      u32 operation,
+				      u64 extensions, int ext_number)
+{
+	u64 __user *address = u64_to_user_ptr(extensions);
+	struct drm_xe_user_extension ext;
+	int err;
+
+	if (XE_IOCTL_DBG(xe, ext_number >= 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, ext.pad) ||
+	    XE_IOCTL_DBG(xe, ext.name >=
+			 ARRAY_SIZE(vm_bind_op_extension_funcs)))
+		return -EINVAL;
+
+	err = vm_bind_op_extension_funcs[ext.name](xe, xef, ops,
+						   operation, extensions);
+	if (XE_IOCTL_DBG(xe, err))
+		return err;
+
+	if (ext.next_extension)
+		return vm_bind_op_user_extensions(xe, xef, ops,
+						  operation, ext.next_extension,
+						  ++ext_number);
+
+	return 0;
+}
+
+static int vm_bind_op_user_extensions_check(struct xe_device *xe,
+					    struct xe_file *xef,
+					    u32 operation, u64 extensions)
+{
+	return vm_bind_op_user_extensions(xe, xef, NULL, operation, extensions, 0);
 }
 
 static struct dma_fence *ops_execute(struct xe_vm *vm,
@@ -2726,6 +2802,7 @@ static int vm_bind_ioctl_ops_execute(struct xe_vm *vm,
 #define ALL_DRM_XE_SYNCS_FLAGS (DRM_XE_SYNCS_FLAG_WAIT_FOR_OP)
 
 static int vm_bind_ioctl_check_args(struct xe_device *xe,
+				    struct xe_file *xef,
 				    struct drm_xe_vm_bind *args,
 				    struct drm_xe_vm_bind_op **bind_ops)
 {
@@ -2769,6 +2846,7 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe,
 		u64 obj_offset = (*bind_ops)[i].obj_offset;
 		u32 prefetch_region = (*bind_ops)[i].prefetch_mem_region_instance;
 		bool is_null = flags & DRM_XE_VM_BIND_FLAG_NULL;
+		u64 extensions = (*bind_ops)[i].extensions;
 		u16 pat_index = (*bind_ops)[i].pat_index;
 		u16 coh_mode;
 
@@ -2829,6 +2907,13 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe,
 			err = -EINVAL;
 			goto free_bind_ops;
 		}
+
+		if (extensions) {
+			err = vm_bind_op_user_extensions_check(xe, xef, op, extensions);
+			if (err)
+				goto free_bind_ops;
+		}
+
 	}
 
 	return 0;
@@ -2940,7 +3025,7 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
 	int err;
 	int i;
 
-	err = vm_bind_ioctl_check_args(xe, args, &bind_ops);
+	err = vm_bind_ioctl_check_args(xe, xef, args, &bind_ops);
 	if (err)
 		return err;
 
@@ -3067,11 +3152,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, bos[i], obj_offset,
 						  addr, range, op, flags,
 						  prefetch_region, pat_index);
-		if (IS_ERR(ops[i])) {
+		if (!IS_ERR(ops[i]) && extensions) {
+			err = vm_bind_op_user_extensions(xe, xef, ops[i],
+							 op, extensions, 0);
+			if (err)
+				goto unwind_ops;
+		} else if (IS_ERR(ops[i])) {
 			err = PTR_ERR(ops[i]);
 			ops[i] = NULL;
 			goto unwind_ops;
diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_types.h
index 557b047ebdd7..1c5776194e54 100644
--- a/drivers/gpu/drm/xe/xe_vm_types.h
+++ b/drivers/gpu/drm/xe/xe_vm_types.h
@@ -70,6 +70,14 @@ struct xe_userptr {
 #endif
 };
 
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+struct xe_eudebug_vma_metadata {
+	struct list_head list;
+};
+#else
+struct xe_eudebug_vma_metadata { };
+#endif
+
 struct xe_vma {
 	/** @gpuva: Base GPUVA object */
 	struct drm_gpuva gpuva;
@@ -121,6 +129,11 @@ struct xe_vma {
 	 * Needs to be signalled before UNMAP can be processed.
 	 */
 	struct xe_user_fence *ufence;
+
+	struct {
+		/** @metadata: List of vma debug metadata */
+		struct xe_eudebug_vma_metadata metadata;
+	} eudebug;
 };
 
 /**
@@ -311,6 +324,10 @@ struct xe_vma_op_map {
 	bool dumpable;
 	/** @pat_index: The pat index to use for this operation. */
 	u16 pat_index;
+	struct  {
+		/** @vma_metadata: List of vma debug metadata */
+		struct xe_eudebug_vma_metadata metadata;
+	} eudebug;
 };
 
 /** struct xe_vma_op_remap - VMA remap operation */
@@ -388,4 +405,14 @@ struct xe_vma_ops {
 #endif
 };
 
+struct xe_vma_debug_metadata {
+	/** @debug.metadata: id of attached xe_debug_metadata */
+	u32 metadata_id;
+	/** @debug.cookie: user defined cookie */
+	u64 cookie;
+
+	/** @link: list of metadata attached to vma */
+	struct list_head link;
+};
+
 #endif
diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h
index 0d75a072b838..2416971fbf2d 100644
--- a/include/uapi/drm/xe_drm.h
+++ b/include/uapi/drm/xe_drm.h
@@ -886,6 +886,23 @@ struct drm_xe_vm_destroy {
 	__u64 reserved[2];
 };
 
+struct drm_xe_vm_bind_op_ext_attach_debug {
+	/** @base: base user extension */
+	struct drm_xe_user_extension base;
+
+	/** @id: Debug object id from create metadata */
+	__u64 metadata_id;
+
+	/** @flags: Flags */
+	__u64 flags;
+
+	/** @cookie: Cookie */
+	__u64 cookie;
+
+	/** @reserved: Reserved */
+	__u64 reserved;
+};
+
 /**
  * struct drm_xe_vm_bind_op - run bind operations
  *
@@ -910,7 +927,9 @@ struct drm_xe_vm_destroy {
  *    handle MBZ, and the BO offset MBZ. This flag is intended to
  *    implement VK sparse bindings.
  */
+
 struct drm_xe_vm_bind_op {
+#define XE_VM_BIND_OP_EXTENSIONS_ATTACH_DEBUG 0
 	/** @extensions: Pointer to the first extension struct, if any */
 	__u64 extensions;
 
-- 
2.34.1


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

* [PATCH 15/18] drm/xe/eudebug: Add debug metadata support for xe_eudebug
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
                   ` (13 preceding siblings ...)
  2024-10-01 14:43 ` [PATCH 14/18] drm/xe: Attach debug metadata to vma Mika Kuoppala
@ 2024-10-01 14:43 ` Mika Kuoppala
  2024-10-01 14:43 ` [PATCH 16/18] drm/xe/eudebug: Implement vm_bind_op discovery Mika Kuoppala
                   ` (5 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Mika Kuoppala @ 2024-10-01 14:43 UTC (permalink / raw)
  To: intel-xe; +Cc: Dominik Grzegorzek, Maciej Patelczyk, Mika Kuoppala

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

Reflect debug metadata resource creation/destroy as events passed to the
debugger. Introduce ioctl allowing to read metadata content on demand.

Each VMA can have multiple metadata attached and it is passed from user
on BIND or it's copied on internal remap.

Xe EU Debugger on VM BIND will inform about VMA metadata attachements
during bind IOCTL sending proper OP event.

v2:  - checkpatch (Maciej, Tilak)
     - struct alignment (Matthew)
     - Kconfig (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>
---
 drivers/gpu/drm/xe/xe_debug_metadata.c |   7 +-
 drivers/gpu/drm/xe/xe_eudebug.c        | 328 ++++++++++++++++++++++++-
 drivers/gpu/drm/xe/xe_eudebug.h        |  13 +-
 drivers/gpu/drm/xe/xe_eudebug_types.h  |  27 +-
 drivers/gpu/drm/xe/xe_vm.c             |   2 +-
 include/uapi/drm/xe_drm_eudebug.h      |  30 +++
 6 files changed, 396 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/xe/xe_debug_metadata.c b/drivers/gpu/drm/xe/xe_debug_metadata.c
index 303640f8ba1f..d02ec99f7ca1 100644
--- a/drivers/gpu/drm/xe/xe_debug_metadata.c
+++ b/drivers/gpu/drm/xe/xe_debug_metadata.c
@@ -9,6 +9,7 @@
 #include <uapi/drm/xe_drm.h>
 
 #include "xe_device.h"
+#include "xe_eudebug.h"
 #include "xe_macros.h"
 #include "xe_vm.h"
 
@@ -158,7 +159,7 @@ int xe_debug_metadata_create_ioctl(struct drm_device *dev,
 	if (XE_IOCTL_DBG(xe, args->extensions))
 		return -EINVAL;
 
-	if (XE_IOCTL_DBG(xe, args->type > DRM_XE_DEBUG_METADATA_PROGRAM_MODULE))
+	if (XE_IOCTL_DBG(xe, args->type >= WORK_IN_PROGRESS_DRM_XE_DEBUG_METADATA_NUM))
 		return -EINVAL;
 
 	if (XE_IOCTL_DBG(xe, !args->user_addr || !args->len))
@@ -197,6 +198,8 @@ int xe_debug_metadata_create_ioctl(struct drm_device *dev,
 	if (err)
 		goto put_mdata;
 
+	xe_eudebug_debug_metadata_create(xef, mdata);
+
 	return 0;
 
 put_mdata:
@@ -222,6 +225,8 @@ int xe_debug_metadata_destroy_ioctl(struct drm_device *dev,
 	if (XE_IOCTL_DBG(xe, !mdata))
 		return -ENOENT;
 
+	xe_eudebug_debug_metadata_destroy(xef, mdata);
+
 	xe_debug_metadata_put(mdata);
 
 	return 0;
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index 6ee06a58b640..368e1a987f7a 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -20,15 +20,18 @@
 
 #include "xe_assert.h"
 #include "xe_bo.h"
+#include "xe_debug_metadata.h"
 #include "xe_device.h"
 #include "xe_debug_metadata.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_guc_exec_queue_types.h"
 #include "xe_hw_engine.h"
 #include "xe_lrc.h"
 #include "xe_macros.h"
@@ -940,7 +943,7 @@ static long xe_eudebug_read_event(struct xe_eudebug *d,
 		u64_to_user_ptr(arg);
 	struct drm_xe_eudebug_event user_event;
 	struct xe_eudebug_event *event;
-	const unsigned int max_event = DRM_XE_EUDEBUG_EVENT_VM_BIND_UFENCE;
+	const unsigned int max_event = DRM_XE_EUDEBUG_EVENT_VM_BIND_OP_METADATA;
 	long ret = 0;
 
 	if (XE_IOCTL_DBG(xe, copy_from_user(&user_event, user_orig, sizeof(user_event))))
@@ -1224,6 +1227,90 @@ static long xe_eudebug_eu_control(struct xe_eudebug *d, const u64 arg)
 	return ret;
 }
 
+static struct xe_debug_metadata *find_metadata_get(struct xe_eudebug *d,
+						   u32 id)
+{
+	struct xe_debug_metadata *m;
+
+	mutex_lock(&d->res->lock);
+	m = find_resource__unlocked(d->res, XE_EUDEBUG_RES_TYPE_METADATA, id);
+	if (m)
+		kref_get(&m->refcount);
+	mutex_unlock(&d->res->lock);
+
+	return m;
+}
+
+static long xe_eudebug_read_metadata(struct xe_eudebug *d,
+				     unsigned int cmd,
+				     const u64 arg)
+{
+	struct drm_xe_eudebug_read_metadata user_arg;
+	struct xe_debug_metadata *mdata;
+	struct xe_file *xef;
+	struct xe_device *xe = d->xe;
+	long ret = 0;
+
+	if (XE_IOCTL_DBG(xe, !(_IOC_DIR(cmd) & _IOC_WRITE)))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, !(_IOC_DIR(cmd) & _IOC_READ)))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, _IOC_SIZE(cmd) < sizeof(user_arg)))
+		return -EINVAL;
+
+	if (copy_from_user(&user_arg, u64_to_user_ptr(arg), sizeof(user_arg)))
+		return -EFAULT;
+
+	if (XE_IOCTL_DBG(xe, user_arg.flags))
+		return -EINVAL;
+
+	if (!access_ok(u64_to_user_ptr(user_arg.ptr), user_arg.size))
+		return -EFAULT;
+
+	if (xe_eudebug_detached(d))
+		return -ENOTCONN;
+
+	eu_dbg(d,
+	       "read metadata: client_handle=%llu, metadata_handle=%llu, flags=0x%x",
+	       user_arg.client_handle, user_arg.metadata_handle, user_arg.flags);
+
+	xef = find_client_get(d, user_arg.client_handle);
+	if (XE_IOCTL_DBG(xe, !xef))
+		return -EINVAL;
+
+	mdata = find_metadata_get(d, (u32)user_arg.metadata_handle);
+	if (XE_IOCTL_DBG(xe, !mdata)) {
+		xe_file_put(xef);
+		return -EINVAL;
+	}
+
+	if (user_arg.size) {
+		if (user_arg.size < mdata->len) {
+			ret = -EINVAL;
+			goto metadata_put;
+		}
+
+		/* This limits us to a maximum payload size of 2G */
+		if (copy_to_user(u64_to_user_ptr(user_arg.ptr),
+				 mdata->ptr, mdata->len)) {
+			ret = -EFAULT;
+			goto metadata_put;
+		}
+	}
+
+	user_arg.size = mdata->len;
+
+	if (copy_to_user(u64_to_user_ptr(arg), &user_arg, sizeof(user_arg)))
+		ret = -EFAULT;
+
+metadata_put:
+	xe_debug_metadata_put(mdata);
+	xe_file_put(xef);
+	return ret;
+}
+
 static long xe_eudebug_vm_open_ioctl(struct xe_eudebug *d, unsigned long arg);
 
 static long xe_eudebug_ioctl(struct file *file,
@@ -1254,7 +1341,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_READ_METADATA:
+		ret = xe_eudebug_read_metadata(d, cmd, arg);
+		eu_dbg(d, "ioctl cmd=READ_METADATA ret=%ld\n", ret);
+		break;
 	default:
 		ret = -EINVAL;
 	}
@@ -2559,19 +2649,145 @@ static int vm_bind_op_event(struct xe_eudebug *d,
 	return xe_eudebug_queue_bind_event(d, vm, event);
 }
 
+static int vm_bind_op_metadata_event(struct xe_eudebug *d,
+				     struct xe_vm *vm,
+				     u32 flags,
+				     u64 ref_seqno,
+				     u64 metadata_handle,
+				     u64 metadata_cookie)
+{
+	struct xe_eudebug_event_vm_bind_op_metadata *e;
+	struct xe_eudebug_event *event;
+	const u32 sz = sizeof(*e);
+	u64 seqno;
+
+	seqno = atomic_long_inc_return(&d->events.seqno);
+
+	event = xe_eudebug_create_event(d, DRM_XE_EUDEBUG_EVENT_VM_BIND_OP_METADATA,
+					seqno, flags, sz, GFP_KERNEL);
+	if (!event)
+		return -ENOMEM;
+
+	e = cast_event(e, event);
+
+	write_member(struct drm_xe_eudebug_event_vm_bind_op_metadata, e,
+		     vm_bind_op_ref_seqno, ref_seqno);
+	write_member(struct drm_xe_eudebug_event_vm_bind_op_metadata, e,
+		     metadata_handle, metadata_handle);
+	write_member(struct drm_xe_eudebug_event_vm_bind_op_metadata, e,
+		     metadata_cookie, metadata_cookie);
+
+	/* If in discovery, no need to collect ops */
+	if (!completion_done(&d->discovery))
+		return xe_eudebug_queue_event(d, event);
+
+	return xe_eudebug_queue_bind_event(d, vm, event);
+}
+
+static int vm_bind_op_metadata_count(struct xe_eudebug *d,
+				     struct xe_vm *vm,
+				     struct list_head *debug_metadata)
+{
+	struct xe_vma_debug_metadata *metadata;
+	struct xe_debug_metadata *mdata;
+	int h_m = 0, metadata_count = 0;
+
+	if (!debug_metadata)
+		return 0;
+
+	list_for_each_entry(metadata, debug_metadata, link) {
+		mdata = xe_debug_metadata_get(vm->xef, metadata->metadata_id);
+		if (mdata) {
+			h_m = find_handle(d->res, XE_EUDEBUG_RES_TYPE_METADATA, mdata);
+			xe_debug_metadata_put(mdata);
+		}
+
+		if (!mdata || h_m < 0) {
+			if (!mdata) {
+				eu_err(d, "Metadata::%u not found.",
+				       metadata->metadata_id);
+			} else {
+				eu_err(d, "Metadata::%u not in the xe debugger",
+				       metadata->metadata_id);
+			}
+			xe_eudebug_disconnect(d, -ENOENT);
+			return -ENOENT;
+		}
+		metadata_count++;
+	}
+	return metadata_count;
+}
+
+static int vm_bind_op_metadata(struct xe_eudebug *d, struct xe_vm *vm,
+			       const u32 flags,
+			       const u64 op_ref_seqno,
+			       struct list_head *debug_metadata)
+{
+	struct xe_vma_debug_metadata *metadata;
+	int h_m = 0; /* handle space range = <1, MAX_INT>, return 0 if metadata not attached */
+	int metadata_count = 0;
+	int ret;
+
+	if (!debug_metadata)
+		return 0;
+
+	XE_WARN_ON(flags != DRM_XE_EUDEBUG_EVENT_CREATE);
+
+	list_for_each_entry(metadata, debug_metadata, link) {
+		struct xe_debug_metadata *mdata;
+
+		mdata = xe_debug_metadata_get(vm->xef, metadata->metadata_id);
+		if (mdata) {
+			h_m = find_handle(d->res, XE_EUDEBUG_RES_TYPE_METADATA, mdata);
+			xe_debug_metadata_put(mdata);
+		}
+
+		if (!mdata || h_m < 0) {
+			eu_err(d, "Attached debug metadata::%u not found!\n",
+			       metadata->metadata_id);
+			return -ENOENT;
+		}
+
+		ret = vm_bind_op_metadata_event(d, vm, flags, op_ref_seqno,
+						h_m, metadata->cookie);
+		if (ret < 0)
+			return ret;
+
+		metadata_count++;
+	}
+
+	return metadata_count;
+}
+
 static int vm_bind_op(struct xe_eudebug *d, struct xe_vm *vm,
 		      const u32 flags, const u64 bind_ref_seqno,
-		      u64 addr, u64 range)
+		      u64 addr, u64 range,
+		      struct list_head *debug_metadata)
 {
 	u64 op_seqno = 0;
-	u64 num_extensions = 0;
+	u64 num_extensions;
 	int ret;
 
+	ret = vm_bind_op_metadata_count(d, vm, debug_metadata);
+	if (ret < 0)
+		return ret;
+
+	num_extensions = ret;
+
 	ret = vm_bind_op_event(d, vm, flags, bind_ref_seqno, num_extensions,
 			       addr, range, &op_seqno);
 	if (ret)
 		return ret;
 
+	ret = vm_bind_op_metadata(d, vm, flags, op_seqno, debug_metadata);
+	if (ret < 0)
+		return ret;
+
+	if (ret != num_extensions) {
+		eu_err(d, "Inconsistency in metadata detected.");
+		return -EINVAL;
+	}
+
 	return 0;
 }
 
@@ -2684,9 +2900,11 @@ void xe_eudebug_vm_bind_start(struct xe_vm *vm)
 	xe_eudebug_put(d);
 }
 
-void xe_eudebug_vm_bind_op_add(struct xe_vm *vm, u32 op, u64 addr, u64 range)
+void xe_eudebug_vm_bind_op_add(struct xe_vm *vm, u32 op, u64 addr, u64 range,
+			       struct drm_gpuva_ops *ops)
 {
 	struct xe_eudebug *d;
+	struct list_head *debug_metadata = NULL;
 	u32 flags;
 
 	if (!xe_vm_in_lr_mode(vm))
@@ -2696,7 +2914,17 @@ void xe_eudebug_vm_bind_op_add(struct xe_vm *vm, u32 op, u64 addr, u64 range)
 	case DRM_XE_VM_BIND_OP_MAP:
 	case DRM_XE_VM_BIND_OP_MAP_USERPTR:
 	{
+		struct drm_gpuva_op *__op;
+
 		flags = DRM_XE_EUDEBUG_EVENT_CREATE;
+
+		/* OP_MAP will be last and singleton */
+		drm_gpuva_for_each_op(__op, ops) {
+			struct xe_vma_op *op = gpuva_op_to_vma_op(__op);
+
+			if (op->base.op == DRM_GPUVA_OP_MAP)
+				debug_metadata = &op->map.vma->eudebug.metadata.list;
+		}
 		break;
 	}
 	case DRM_XE_VM_BIND_OP_UNMAP:
@@ -2715,7 +2943,8 @@ void xe_eudebug_vm_bind_op_add(struct xe_vm *vm, u32 op, u64 addr, u64 range)
 	if (!d)
 		return;
 
-	xe_eudebug_event_put(d, vm_bind_op(d, vm, flags, 0, addr, range));
+	xe_eudebug_event_put(d, vm_bind_op(d, vm, flags, 0, addr, range,
+					   debug_metadata));
 }
 
 static struct xe_eudebug_event *fetch_bind_event(struct xe_vm * const vm)
@@ -2844,8 +3073,89 @@ int xe_eudebug_vm_bind_ufence(struct xe_user_fence *ufence)
 	return err;
 }
 
+static int send_debug_metadata_event(struct xe_eudebug *d, u32 flags,
+				     u64 client_handle, u64 metadata_handle,
+				     u64 type, u64 len, u64 seqno)
+{
+	struct xe_eudebug_event *event;
+	struct xe_eudebug_event_metadata *e;
+
+	event = xe_eudebug_create_event(d, DRM_XE_EUDEBUG_EVENT_METADATA, seqno,
+					flags, sizeof(*e), GFP_KERNEL);
+	if (!event)
+		return -ENOMEM;
+
+	e = cast_event(e, event);
+
+	write_member(struct drm_xe_eudebug_event_metadata, e, client_handle, client_handle);
+	write_member(struct drm_xe_eudebug_event_metadata, e, metadata_handle, metadata_handle);
+	write_member(struct drm_xe_eudebug_event_metadata, e, type, type);
+	write_member(struct drm_xe_eudebug_event_metadata, e, len, len);
+
+	return xe_eudebug_queue_event(d, event);
+}
+
+static int debug_metadata_create_event(struct xe_eudebug *d,
+				       struct xe_file *xef, struct xe_debug_metadata *m)
+{
+	int h_c, h_m;
+	u64 seqno;
+
+	h_c = find_handle(d->res, XE_EUDEBUG_RES_TYPE_CLIENT, xef);
+	if (h_c < 0)
+		return h_c;
+
+	h_m = xe_eudebug_add_handle(d, XE_EUDEBUG_RES_TYPE_METADATA, m, &seqno);
+	if (h_m <= 0)
+		return h_m;
+
+	return send_debug_metadata_event(d, DRM_XE_EUDEBUG_EVENT_CREATE,
+					 h_c, h_m, m->type, m->len, seqno);
+}
+
+static int debug_metadata_destroy_event(struct xe_eudebug *d,
+					struct xe_file *xef, struct xe_debug_metadata *m)
+{
+	int h_c, h_m;
+	u64 seqno;
+
+	h_c = find_handle(d->res, XE_EUDEBUG_RES_TYPE_CLIENT, xef);
+	if (h_c < 0)
+		return h_c;
+
+	h_m = xe_eudebug_remove_handle(d, XE_EUDEBUG_RES_TYPE_METADATA, m, &seqno);
+	if (h_m < 0)
+		return h_m;
+
+	return send_debug_metadata_event(d, DRM_XE_EUDEBUG_EVENT_DESTROY,
+					 h_c, h_m, m->type, m->len, seqno);
+}
+
+void xe_eudebug_debug_metadata_create(struct xe_file *xef, struct xe_debug_metadata *m)
+{
+	struct xe_eudebug *d;
+
+	d = xe_eudebug_get(xef);
+	if (!d)
+		return;
+
+	xe_eudebug_event_put(d, debug_metadata_create_event(d, xef, m));
+}
+
+void xe_eudebug_debug_metadata_destroy(struct xe_file *xef, struct xe_debug_metadata *m)
+{
+	struct xe_eudebug *d;
+
+	d = xe_eudebug_get(xef);
+	if (!d)
+		return;
+
+	xe_eudebug_event_put(d, debug_metadata_destroy_event(d, xef, m));
+}
+
 static int discover_client(struct xe_eudebug *d, struct xe_file *xef)
 {
+	struct xe_debug_metadata *m;
 	struct xe_exec_queue *q;
 	struct xe_vm *vm;
 	unsigned long i;
@@ -2855,6 +3165,12 @@ static int discover_client(struct xe_eudebug *d, struct xe_file *xef)
 	if (err)
 		return err;
 
+	xa_for_each(&xef->eudebug.metadata.xa, i, m) {
+		err = debug_metadata_create_event(d, xef, m);
+		if (err)
+			break;
+	}
+
 	xa_for_each(&xef->vm.xa, i, vm) {
 		err = vm_create_event(d, xef, vm);
 		if (err)
diff --git a/drivers/gpu/drm/xe/xe_eudebug.h b/drivers/gpu/drm/xe/xe_eudebug.h
index ae720a1b05d9..766d0a94d200 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.h
+++ b/drivers/gpu/drm/xe/xe_eudebug.h
@@ -16,6 +16,8 @@ struct xe_vma;
 struct xe_exec_queue;
 struct xe_hw_engine;
 struct xe_user_fence;
+struct xe_debug_metadata;
+struct drm_gpuva_ops;
 
 #if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
 
@@ -39,7 +41,8 @@ void xe_eudebug_exec_queue_destroy(struct xe_file *xef, struct xe_exec_queue *q)
 
 void xe_eudebug_vm_init(struct xe_vm *vm);
 void xe_eudebug_vm_bind_start(struct xe_vm *vm);
-void xe_eudebug_vm_bind_op_add(struct xe_vm *vm, u32 op, u64 addr, u64 range);
+void xe_eudebug_vm_bind_op_add(struct xe_vm *vm, u32 op, u64 addr, u64 range,
+			       struct drm_gpuva_ops *ops);
 void xe_eudebug_vm_bind_end(struct xe_vm *vm, bool has_ufence, int err);
 
 int xe_eudebug_vm_bind_ufence(struct xe_user_fence *ufence);
@@ -49,6 +52,9 @@ void xe_eudebug_ufence_fini(struct xe_user_fence *ufence);
 struct xe_eudebug *xe_eudebug_get(struct xe_file *xef);
 void xe_eudebug_put(struct xe_eudebug *d);
 
+void xe_eudebug_debug_metadata_create(struct xe_file *xef, struct xe_debug_metadata *m);
+void xe_eudebug_debug_metadata_destroy(struct xe_file *xef, struct xe_debug_metadata *m);
+
 #else
 
 static inline int xe_eudebug_connect_ioctl(struct drm_device *dev,
@@ -71,7 +77,7 @@ static inline void xe_eudebug_exec_queue_destroy(struct xe_file *xef, struct xe_
 
 static inline void xe_eudebug_vm_init(struct xe_vm *vm) { }
 static inline void xe_eudebug_vm_bind_start(struct xe_vm *vm) { }
-static inline void xe_eudebug_vm_bind_op_add(struct xe_vm *vm, u32 op, u64 addr, u64 range) { }
+static inline void xe_eudebug_vm_bind_op_add(struct xe_vm *vm, u32 op, u64 addr, u64 range, struct drm_gpuva_ops *ops) { }
 static inline void xe_eudebug_vm_bind_end(struct xe_vm *vm, bool has_ufence, int err) { }
 
 static inline int xe_eudebug_vm_bind_ufence(struct xe_user_fence *ufence) { return 0; }
@@ -81,6 +87,9 @@ static inline void xe_eudebug_ufence_fini(struct xe_user_fence *ufence) { }
 static inline struct xe_eudebug *xe_eudebug_get(struct xe_file *xef) { return NULL; }
 static inline void xe_eudebug_put(struct xe_eudebug *d) { }
 
+static inline void xe_eudebug_debug_metadata_create(struct xe_file *xef, struct xe_debug_metadata *m) { }
+static inline void xe_eudebug_debug_metadata_destroy(struct xe_file *xef, struct xe_debug_metadata *m) { }
+
 #endif /* CONFIG_DRM_XE_EUDEBUG */
 
 #endif
diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
index aae7bc476c7e..b79d4c078216 100644
--- a/drivers/gpu/drm/xe/xe_eudebug_types.h
+++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
@@ -56,7 +56,8 @@ struct xe_eudebug_resource {
 #define XE_EUDEBUG_RES_TYPE_VM		1
 #define XE_EUDEBUG_RES_TYPE_EXEC_QUEUE	2
 #define XE_EUDEBUG_RES_TYPE_LRC		3
-#define XE_EUDEBUG_RES_TYPE_COUNT	(XE_EUDEBUG_RES_TYPE_LRC + 1)
+#define XE_EUDEBUG_RES_TYPE_METADATA	4
+#define XE_EUDEBUG_RES_TYPE_COUNT	(XE_EUDEBUG_RES_TYPE_METADATA + 1)
 
 /**
  * struct xe_eudebug_resources - eudebug resources for all types
@@ -300,4 +301,28 @@ struct xe_eudebug_event_vm_bind_ufence {
 	u64 vm_bind_ref_seqno;
 };
 
+struct xe_eudebug_event_metadata {
+	struct xe_eudebug_event base;
+
+	/** @client_handle: client for the attention */
+	u64 client_handle;
+
+	/** @metadata_handle: debug metadata handle it's created/destroyed */
+	u64 metadata_handle;
+
+	/* @type: metadata type, refer to xe_drm.h for options */
+	u64 type;
+
+	/* @len: size of metadata paylad */
+	u64 len;
+};
+
+struct xe_eudebug_event_vm_bind_op_metadata {
+	struct xe_eudebug_event base;
+	u64 vm_bind_op_ref_seqno;
+
+	u64 metadata_handle;
+	u64 metadata_cookie;
+};
+
 #endif
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index 8c48223892c6..24a7ee1a6e58 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -3172,7 +3172,7 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
 		if (err)
 			goto unwind_ops;
 
-		xe_eudebug_vm_bind_op_add(vm, op, addr, range);
+		xe_eudebug_vm_bind_op_add(vm, op, addr, range, ops[i]);
 
 #ifdef TEST_VM_OPS_ERROR
 		if (flags & FORCE_OP_ERROR) {
diff --git a/include/uapi/drm/xe_drm_eudebug.h b/include/uapi/drm/xe_drm_eudebug.h
index 9917280400d6..2ebf21e15f5b 100644
--- a/include/uapi/drm/xe_drm_eudebug.h
+++ b/include/uapi/drm/xe_drm_eudebug.h
@@ -19,6 +19,7 @@ extern "C" {
 #define DRM_XE_EUDEBUG_IOCTL_EU_CONTROL		_IOWR('j', 0x2, struct drm_xe_eudebug_eu_control)
 #define DRM_XE_EUDEBUG_IOCTL_ACK_EVENT		_IOW('j', 0x4, struct drm_xe_eudebug_ack_event)
 #define DRM_XE_EUDEBUG_IOCTL_VM_OPEN		_IOW('j', 0x1, struct drm_xe_eudebug_vm_open)
+#define DRM_XE_EUDEBUG_IOCTL_READ_METADATA	_IOWR('j', 0x3, struct drm_xe_eudebug_read_metadata)
 
 /* XXX: Document events to match their internal counterparts when moved to xe_drm.h */
 struct drm_xe_eudebug_event {
@@ -34,6 +35,8 @@ struct drm_xe_eudebug_event {
 #define DRM_XE_EUDEBUG_EVENT_VM_BIND		6
 #define DRM_XE_EUDEBUG_EVENT_VM_BIND_OP		7
 #define DRM_XE_EUDEBUG_EVENT_VM_BIND_UFENCE	8
+#define DRM_XE_EUDEBUG_EVENT_METADATA		9
+#define DRM_XE_EUDEBUG_EVENT_VM_BIND_OP_METADATA 10
 
 	__u16 flags;
 #define DRM_XE_EUDEBUG_EVENT_CREATE		(1 << 0)
@@ -188,6 +191,33 @@ struct drm_xe_eudebug_vm_open {
 	__u64 timeout_ns;
 };
 
+struct drm_xe_eudebug_read_metadata {
+	__u64 client_handle;
+	__u64 metadata_handle;
+	__u32 flags;
+	__u32 reserved;
+	__u64 ptr;
+	__u64 size;
+};
+
+struct drm_xe_eudebug_event_metadata {
+	struct drm_xe_eudebug_event base;
+
+	__u64 client_handle;
+	__u64 metadata_handle;
+	/* XXX: Refer to xe_drm.h for fields */
+	__u64 type;
+	__u64 len;
+};
+
+struct drm_xe_eudebug_event_vm_bind_op_metadata {
+	struct drm_xe_eudebug_event base;
+	__u64 vm_bind_op_ref_seqno; /* *_event_vm_bind_op.base.seqno */
+
+	__u64 metadata_handle;
+	__u64 metadata_cookie;
+};
+
 #if defined(__cplusplus)
 }
 #endif
-- 
2.34.1


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

* [PATCH 16/18] drm/xe/eudebug: Implement vm_bind_op discovery
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
                   ` (14 preceding siblings ...)
  2024-10-01 14:43 ` [PATCH 15/18] drm/xe/eudebug: Add debug metadata support for xe_eudebug Mika Kuoppala
@ 2024-10-01 14:43 ` Mika Kuoppala
  2024-10-01 14:43 ` [PATCH 17/18] drm/xe/eudebug: Dynamically toggle debugger functionality Mika Kuoppala
                   ` (4 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Mika Kuoppala @ 2024-10-01 14:43 UTC (permalink / raw)
  To: intel-xe; +Cc: Mika Kuoppala, Matthew Brost

Follow the vm bind, vm_bind op sequence for
discovery process of a vm with the vmas it has.
Send events for ops and attach metadata if available.

v2: - Fix bad op ref seqno (Christoph)
    - with discovery semaphore, we dont need vm lock (Matthew)

Cc: Matthew Brost <matthew.brost@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 drivers/gpu/drm/xe/xe_eudebug.c | 45 +++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index 368e1a987f7a..faa49ba55b78 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -3153,6 +3153,47 @@ void xe_eudebug_debug_metadata_destroy(struct xe_file *xef, struct xe_debug_meta
 	xe_eudebug_event_put(d, debug_metadata_destroy_event(d, xef, m));
 }
 
+static int vm_discover_binds(struct xe_eudebug *d, struct xe_vm *vm)
+{
+	struct drm_gpuva *va;
+	unsigned int num_ops = 0, send_ops = 0;
+	u64 ref_seqno = 0;
+	int err;
+
+	/*
+	 * Currently only vm_bind_ioctl inserts vma's
+	 * and with discovery lock, we have exclusivity.
+	 */
+	lockdep_assert_held_write(&d->xe->eudebug.discovery_lock);
+
+	drm_gpuvm_for_each_va(va, &vm->gpuvm)
+		num_ops++;
+
+	if (!num_ops)
+		return 0;
+
+	err = vm_bind_event(d, vm, num_ops, &ref_seqno);
+	if (err)
+		return err;
+
+	drm_gpuvm_for_each_va(va, &vm->gpuvm) {
+		struct xe_vma *vma = container_of(va, struct xe_vma, gpuva);
+
+		if (send_ops >= num_ops)
+			break;
+
+		err = vm_bind_op(d, vm, DRM_XE_EUDEBUG_EVENT_CREATE, ref_seqno,
+				 xe_vma_start(vma), xe_vma_size(vma),
+				 &vma->eudebug.metadata.list);
+		if (err)
+			return err;
+
+		send_ops++;
+	}
+
+	return num_ops == send_ops ? 0 : -EINVAL;
+}
+
 static int discover_client(struct xe_eudebug *d, struct xe_file *xef)
 {
 	struct xe_debug_metadata *m;
@@ -3175,6 +3216,10 @@ static int discover_client(struct xe_eudebug *d, struct xe_file *xef)
 		err = vm_create_event(d, xef, vm);
 		if (err)
 			break;
+
+		err = vm_discover_binds(d, vm);
+		if (err)
+			break;
 	}
 
 	xa_for_each(&xef->exec_queue.xa, i, q) {
-- 
2.34.1


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

* [PATCH 17/18] drm/xe/eudebug: Dynamically toggle debugger functionality
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
                   ` (15 preceding siblings ...)
  2024-10-01 14:43 ` [PATCH 16/18] drm/xe/eudebug: Implement vm_bind_op discovery Mika Kuoppala
@ 2024-10-01 14:43 ` Mika Kuoppala
  2024-10-01 14:43 ` [PATCH 18/18] drm/xe/eudebug_test: Introduce xe_eudebug wa kunit test Mika Kuoppala
                   ` (3 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Mika Kuoppala @ 2024-10-01 14:43 UTC (permalink / raw)
  To: intel-xe
  Cc: Christoph Manszewski, Dominik Grzegorzek, Maciej Patelczyk,
	Mika Kuoppala

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

Make it possible to dynamically enable/disable debugger funtionality,
including the setting and unsetting of required hw register values via a
sysfs entry located at '/sys/class/drm/card<X>/device/enable_eudebug'.

This entry uses 'kstrtobool' and as such it accepts inputs as documented
by this function, in particular '0' and '1'.

v2: use new discovery_lock to gain exclusivity (Mika)

Signed-off-by: Christoph Manszewski <christoph.manszewski@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>
---
 drivers/gpu/drm/xe/xe_device.c       |   2 -
 drivers/gpu/drm/xe/xe_device_types.h |   3 +
 drivers/gpu/drm/xe/xe_eudebug.c      | 125 +++++++++++++++++++++++----
 drivers/gpu/drm/xe/xe_eudebug.h      |   2 -
 drivers/gpu/drm/xe/xe_exec_queue.c   |   5 ++
 drivers/gpu/drm/xe/xe_hw_engine.c    |   1 -
 drivers/gpu/drm/xe/xe_reg_sr.c       |  21 +++--
 drivers/gpu/drm/xe/xe_reg_sr.h       |   4 +-
 drivers/gpu/drm/xe/xe_rtp.c          |   2 +-
 9 files changed, 133 insertions(+), 32 deletions(-)

diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index e0d55e11ace6..0647c17a79dd 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -781,8 +781,6 @@ int xe_device_probe(struct xe_device *xe)
 
 	xe_debugfs_register(xe);
 
-	xe_eudebug_init_late(xe);
-
 	xe_hwmon_register(xe);
 
 	for_each_gt(gt, xe, id)
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index 40b0f61b64b8..fc448d44752e 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -549,6 +549,9 @@ struct xe_device {
 		/** discovery_lock: used for discovery to block xe ioctls */
 		struct rw_semaphore discovery_lock;
 
+		/** @enable: is the debugging functionality enabled */
+		bool enable;
+
 		/** @attention_scan: attention scan worker */
 		struct delayed_work attention_scan;
 	} eudebug;
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index faa49ba55b78..6e26245556d4 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -2000,9 +2000,6 @@ xe_eudebug_connect(struct xe_device *xe,
 
 	param->version = DRM_XE_EUDEBUG_VERSION;
 
-	if (!xe->eudebug.available)
-		return -EOPNOTSUPP;
-
 	d = kzalloc(sizeof(*d), GFP_KERNEL);
 	if (!d)
 		return -ENOMEM;
@@ -2062,17 +2059,19 @@ int xe_eudebug_connect_ioctl(struct drm_device *dev,
 {
 	struct xe_device *xe = to_xe_device(dev);
 	struct drm_xe_eudebug_connect * const param = data;
-	int ret = 0;
 
-	ret = xe_eudebug_connect(xe, param);
+	lockdep_assert_held(&xe->eudebug.discovery_lock);
 
-	return ret;
+	if (!xe->eudebug.enable)
+		return -ENODEV;
+
+	return xe_eudebug_connect(xe, param);
 }
 
 #undef XE_REG_MCR
 #define XE_REG_MCR(...)     XE_REG(__VA_ARGS__, .mcr = 1)
 
-void xe_eudebug_init_hw_engine(struct xe_hw_engine *hwe)
+static void xe_eudebug_reinit_hw_engine(struct xe_hw_engine *hwe, bool enable)
 {
 	struct xe_gt *gt = hwe->gt;
 	struct xe_device *xe = gt_to_xe(gt);
@@ -2087,22 +2086,22 @@ void xe_eudebug_init_hw_engine(struct xe_hw_engine *hwe)
 		struct xe_reg_sr_entry sr_entry = {
 			.reg = ROW_CHICKEN,
 			.clr_bits = STALL_DOP_GATING_DISABLE,
-			.set_bits = STALL_DOP_GATING_DISABLE,
+			.set_bits = enable ? STALL_DOP_GATING_DISABLE : 0,
 			.read_mask = STALL_DOP_GATING_DISABLE,
 		};
 
-		xe_reg_sr_add(&hwe->reg_sr, &sr_entry, gt);
+		xe_reg_sr_add(&hwe->reg_sr, &sr_entry, gt, true);
 	}
 
 	if (XE_WA(gt, 14015474168)) {
 		struct xe_reg_sr_entry sr_entry = {
 			.reg = ROW_CHICKEN2,
 			.clr_bits = XEHPC_DISABLE_BTB,
-			.set_bits = XEHPC_DISABLE_BTB,
+			.set_bits = enable ? XEHPC_DISABLE_BTB : 0,
 			.read_mask = XEHPC_DISABLE_BTB,
 		};
 
-		xe_reg_sr_add(&hwe->reg_sr, &sr_entry, gt);
+		xe_reg_sr_add(&hwe->reg_sr, &sr_entry, gt, true);
 	}
 
 	if (xe->info.graphics_verx100 >= 1200) {
@@ -2112,27 +2111,112 @@ void xe_eudebug_init_hw_engine(struct xe_hw_engine *hwe)
 		struct xe_reg_sr_entry sr_entry = {
 			.reg = TD_CTL,
 			.clr_bits = mask,
-			.set_bits = mask,
+			.set_bits = enable ? mask : 0,
 			.read_mask = mask,
 		};
 
-		xe_reg_sr_add(&hwe->reg_sr, &sr_entry, gt);
+		xe_reg_sr_add(&hwe->reg_sr, &sr_entry, gt, true);
 	}
 
 	if (xe->info.graphics_verx100 >= 1250) {
 		struct xe_reg_sr_entry sr_entry = {
 			.reg = TD_CTL,
 			.clr_bits = TD_CTL_GLOBAL_DEBUG_ENABLE,
-			.set_bits = TD_CTL_GLOBAL_DEBUG_ENABLE,
+			.set_bits = enable ? TD_CTL_GLOBAL_DEBUG_ENABLE : 0,
 			.read_mask = TD_CTL_GLOBAL_DEBUG_ENABLE,
 		};
 
-		xe_reg_sr_add(&hwe->reg_sr, &sr_entry, gt);
+		xe_reg_sr_add(&hwe->reg_sr, &sr_entry, gt, true);
+	}
+}
+
+static int xe_eudebug_enable(struct xe_device *xe, bool enable)
+{
+	struct xe_gt *gt;
+	int i;
+	u8 id;
+
+	if (!xe->eudebug.available)
+		return -EOPNOTSUPP;
+
+	/*
+	 * The connect ioctl has read lock so we can
+	 * serialize with taking write
+	 */
+	down_write(&xe->eudebug.discovery_lock);
+
+	if (!enable && !list_empty(&xe->eudebug.list)) {
+		up_write(&xe->eudebug.discovery_lock);
+		return -EBUSY;
+	}
+
+	if (enable == xe->eudebug.enable) {
+		up_write(&xe->eudebug.discovery_lock);
+		return 0;
 	}
+
+	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_reinit_hw_engine(&gt->hw_engines[i], enable);
+		}
+
+		xe_gt_reset_async(gt);
+		flush_work(&gt->reset.worker);
+	}
+
+	xe->eudebug.enable = enable;
+	up_write(&xe->eudebug.discovery_lock);
+
+	if (enable)
+		attention_scan_flush(xe);
+	else
+		attention_scan_cancel(xe);
+
+	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.enable);
+}
+
+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;
+
+	sysfs_remove_file(&xe->drm.dev->kobj, &dev_attr_enable_eudebug.attr);
 }
 
 void xe_eudebug_init(struct xe_device *xe)
 {
+	struct device *dev = xe->drm.dev;
+	int ret;
+
 	spin_lock_init(&xe->eudebug.lock);
 	INIT_LIST_HEAD(&xe->eudebug.list);
 	INIT_LIST_HEAD(&xe->clients.list);
@@ -2141,14 +2225,17 @@ void xe_eudebug_init(struct xe_device *xe)
 
 	xe->eudebug.ordered_wq = alloc_ordered_workqueue("xe-eudebug-ordered-wq", 0);
 	xe->eudebug.available = !!xe->eudebug.ordered_wq;
-}
 
-void xe_eudebug_init_late(struct xe_device *xe)
-{
 	if (!xe->eudebug.available)
 		return;
 
-	attention_scan_flush(xe);
+	ret = sysfs_create_file(&xe->drm.dev->kobj, &dev_attr_enable_eudebug.attr);
+	if (ret)
+		drm_warn(&xe->drm, "eudebug sysfs init failed: %d, debugger unavailable\n", ret);
+	else
+		devm_add_action_or_reset(dev, xe_eudebug_sysfs_fini, xe);
+
+	xe->eudebug.available = ret == 0;
 }
 
 void xe_eudebug_fini(struct xe_device *xe)
diff --git a/drivers/gpu/drm/xe/xe_eudebug.h b/drivers/gpu/drm/xe/xe_eudebug.h
index 766d0a94d200..403f52148da3 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.h
+++ b/drivers/gpu/drm/xe/xe_eudebug.h
@@ -26,9 +26,7 @@ int xe_eudebug_connect_ioctl(struct drm_device *dev,
 			     struct drm_file *file);
 
 void xe_eudebug_init(struct xe_device *xe);
-void xe_eudebug_init_late(struct xe_device *xe);
 void xe_eudebug_fini(struct xe_device *xe);
-void xe_eudebug_init_hw_engine(struct xe_hw_engine *hwe);
 
 void xe_eudebug_file_open(struct xe_file *xef);
 void xe_eudebug_file_close(struct xe_file *xef);
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
index c2d6993ac103..011d8bb477d4 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.c
+++ b/drivers/gpu/drm/xe/xe_exec_queue.c
@@ -422,6 +422,11 @@ static int exec_queue_set_eudebug(struct xe_device *xe, struct xe_exec_queue *q,
 			 !(value & DRM_XE_EXEC_QUEUE_EUDEBUG_FLAG_ENABLE)))
 		return -EINVAL;
 
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+	if (XE_IOCTL_DBG(xe, !xe->eudebug.enable))
+		return -EPERM;
+#endif
+
 	q->eudebug_flags = EXEC_QUEUE_EUDEBUG_FLAG_ENABLE;
 	q->sched_props.preempt_timeout_us = 0;
 
diff --git a/drivers/gpu/drm/xe/xe_hw_engine.c b/drivers/gpu/drm/xe/xe_hw_engine.c
index 8e9a08395042..28c8cabf508c 100644
--- a/drivers/gpu/drm/xe/xe_hw_engine.c
+++ b/drivers/gpu/drm/xe/xe_hw_engine.c
@@ -558,7 +558,6 @@ static void hw_engine_init_early(struct xe_gt *gt, struct xe_hw_engine *hwe,
 	xe_tuning_process_engine(hwe);
 	xe_wa_process_engine(hwe);
 	hw_engine_setup_default_state(hwe);
-	xe_eudebug_init_hw_engine(hwe);
 
 	xe_reg_sr_init(&hwe->reg_whitelist, hwe->name, gt_to_xe(gt));
 	xe_reg_whitelist_process_engine(hwe);
diff --git a/drivers/gpu/drm/xe/xe_reg_sr.c b/drivers/gpu/drm/xe/xe_reg_sr.c
index 191cb4121acd..023989adbe05 100644
--- a/drivers/gpu/drm/xe/xe_reg_sr.c
+++ b/drivers/gpu/drm/xe/xe_reg_sr.c
@@ -93,22 +93,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 51fbba423e27..d67fafdcd847 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
  */
@@ -21,7 +23,7 @@ int xe_reg_sr_init(struct xe_reg_sr *sr, const char *name, struct xe_device *xe)
 void xe_reg_sr_dump(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_rtp.c b/drivers/gpu/drm/xe/xe_rtp.c
index b13d4d62f0b1..6006f7c90cac 100644
--- a/drivers/gpu/drm/xe/xe_rtp.c
+++ b/drivers/gpu/drm/xe/xe_rtp.c
@@ -153,7 +153,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,
-- 
2.34.1


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

* [PATCH 18/18] drm/xe/eudebug_test: Introduce xe_eudebug wa kunit test
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
                   ` (16 preceding siblings ...)
  2024-10-01 14:43 ` [PATCH 17/18] drm/xe/eudebug: Dynamically toggle debugger functionality Mika Kuoppala
@ 2024-10-01 14:43 ` Mika Kuoppala
  2024-10-01 15:02 ` ✓ CI.Patch_applied: success for GPU debug support (eudebug) (rev2) Patchwork
                   ` (2 subsequent siblings)
  20 siblings, 0 replies; 40+ messages in thread
From: Mika Kuoppala @ 2024-10-01 14:43 UTC (permalink / raw)
  To: intel-xe; +Cc: Christoph Manszewski, 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)

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       | 175 ++++++++++++++++++++
 drivers/gpu/drm/xe/tests/xe_live_test_mod.c |   5 +
 drivers/gpu/drm/xe/xe_eudebug.c             |   4 +
 3 files changed, 184 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..3e78b6a9d056
--- /dev/null
+++ b/drivers/gpu/drm/xe/tests/xe_eudebug.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0 AND MIT
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#include <kunit/visibility.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;
+	u8 id;
+	int ret = 0;
+
+	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;
+
+		ret = xe_force_wake_get(gt_to_fw(gt), XE_FW_RENDER);
+		KUNIT_ASSERT_EQ_MSG(test, ret, 0, "Forcewake failed.\n");
+
+		__check_regs(gt, enable_eudebug);
+
+		xe_force_wake_put(gt_to_fw(gt), XE_FW_RENDER);
+	}
+	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.enable;
+
+	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 5f14737c8210..7dd8a0a4bdfd 100644
--- a/drivers/gpu/drm/xe/tests/xe_live_test_mod.c
+++ b/drivers/gpu/drm/xe/tests/xe_live_test_mod.c
@@ -15,6 +15,11 @@ kunit_test_suite(xe_dma_buf_test_suite);
 kunit_test_suite(xe_migrate_test_suite);
 kunit_test_suite(xe_mocs_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 6e26245556d4..4d81955d0169 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -3885,3 +3885,7 @@ xe_eudebug_vm_open_ioctl(struct xe_eudebug *d, unsigned long arg)
 
 	return ret;
 }
+
+#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
+#include "tests/xe_eudebug.c"
+#endif
-- 
2.34.1


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

* ✓ CI.Patch_applied: success for GPU debug support (eudebug) (rev2)
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
                   ` (17 preceding siblings ...)
  2024-10-01 14:43 ` [PATCH 18/18] drm/xe/eudebug_test: Introduce xe_eudebug wa kunit test Mika Kuoppala
@ 2024-10-01 15:02 ` Patchwork
  2024-10-01 15:03 ` ✗ CI.checkpatch: warning " Patchwork
  2024-10-01 15:03 ` ✗ CI.KUnit: failure " Patchwork
  20 siblings, 0 replies; 40+ messages in thread
From: Patchwork @ 2024-10-01 15:02 UTC (permalink / raw)
  To: Mika Kuoppala; +Cc: intel-xe

== Series Details ==

Series: GPU debug support (eudebug) (rev2)
URL   : https://patchwork.freedesktop.org/series/136572/
State : success

== Summary ==

=== Applying kernel patches on branch 'drm-tip' with base: ===
Base commit: f3f61bfc1cdd drm-tip: 2024y-10m-01d-11h-59m-39s UTC integration manifest
=== git am output follows ===
Applying: ptrace: export ptrace_may_access
Applying: drm/xe/eudebug: Introduce eudebug support
Applying: drm/xe/eudebug: Introduce discovery for resources
Applying: drm/xe/eudebug: Introduce exec_queue events
Applying: drm/xe/eudebug: hw enablement for eudebug
Applying: drm/xe: Add EUDEBUG_ENABLE exec queue property
Applying: drm/xe/eudebug: Introduce per device attention scan worker
Applying: drm/xe/eudebug: Introduce EU control interface
Applying: drm/xe/eudebug: Add vm bind and vm bind ops
Applying: drm/xe/eudebug: Add UFENCE events with acks
Applying: drm/xe/eudebug: vm open/pread/pwrite
Applying: drm/xe/eudebug: implement userptr_vma access
Applying: drm/xe: Debug metadata create/destroy ioctls
Applying: drm/xe: Attach debug metadata to vma
Applying: drm/xe/eudebug: Add debug metadata support for xe_eudebug
Applying: drm/xe/eudebug: Implement vm_bind_op discovery
Applying: drm/xe/eudebug: Dynamically toggle debugger functionality
Applying: drm/xe/eudebug_test: Introduce xe_eudebug wa kunit test



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

* ✗ CI.checkpatch: warning for GPU debug support (eudebug) (rev2)
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
                   ` (18 preceding siblings ...)
  2024-10-01 15:02 ` ✓ CI.Patch_applied: success for GPU debug support (eudebug) (rev2) Patchwork
@ 2024-10-01 15:03 ` Patchwork
  2024-10-01 15:03 ` ✗ CI.KUnit: failure " Patchwork
  20 siblings, 0 replies; 40+ messages in thread
From: Patchwork @ 2024-10-01 15:03 UTC (permalink / raw)
  To: Mika Kuoppala; +Cc: intel-xe

== Series Details ==

Series: GPU debug support (eudebug) (rev2)
URL   : https://patchwork.freedesktop.org/series/136572/
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
30ab6715fc09baee6cc14cb3c89ad8858688d474
+ cd /kernel
+ git config --global --add safe.directory /kernel
+ git log -n1
commit fefc940eb695b6ace9e906fbe1869fd9b7f658dd
Author: Christoph Manszewski <christoph.manszewski@intel.com>
Date:   Tue Oct 1 17:43:06 2024 +0300

    drm/xe/eudebug_test: Introduce xe_eudebug wa kunit test
    
    Introduce kunit test for eudebug. For now it checks the dynamic
    application of WAs.
    
    v2: adapt to removal of call_for_each_device (Mika)
    
    Signed-off-by: Christoph Manszewski <christoph.manszewski@intel.com>
    Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
+ /mt/dim checkpatch f3f61bfc1cddded910b3916aa2c576a14bf62f71 drm-intel
cfddad185c79 ptrace: export ptrace_may_access
6de3ddf0c385 drm/xe/eudebug: Introduce eudebug support
-:202: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#202: 
new file mode 100644

-:240: ERROR:COMPLEX_MACRO: Macros with complex values should be enclosed in parentheses
#240: FILE: drivers/gpu/drm/xe/xe_eudebug.c:34:
+#define XE_EUDEBUG_DBG_ARGS(d) (d)->session, \
+		atomic_long_read(&(d)->events.seqno), \
+		READ_ONCE(d->connection.status) <= 0 ? "disconnected" : "", \
+		current->pid, \
+		task_tgid_nr(current), \
+		(d)->target_task->pid, \
+		task_tgid_nr((d)->target_task)

-:240: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'd' - possible side-effects?
#240: FILE: drivers/gpu/drm/xe/xe_eudebug.c:34:
+#define XE_EUDEBUG_DBG_ARGS(d) (d)->session, \
+		atomic_long_read(&(d)->events.seqno), \
+		READ_ONCE(d->connection.status) <= 0 ? "disconnected" : "", \
+		current->pid, \
+		task_tgid_nr(current), \
+		(d)->target_task->pid, \
+		task_tgid_nr((d)->target_task)

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

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

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

-:257: CHECK:MACRO_ARG_PRECEDENCE: Macro argument 'T' may be better as '(T)' to avoid precedence issues
#257: FILE: drivers/gpu/drm/xe/xe_eudebug.c:51:
+#define struct_member(T, member) (((T *)0)->member)

-:257: CHECK:MACRO_ARG_PRECEDENCE: Macro argument 'member' may be better as '(member)' to avoid precedence issues
#257: FILE: drivers/gpu/drm/xe/xe_eudebug.c:51:
+#define struct_member(T, member) (((T *)0)->member)

-:260: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'ptr' - possible side-effects?
#260: FILE: drivers/gpu/drm/xe/xe_eudebug.c:54:
+#define write_member(T_out, ptr, member, value) { \
+	BUILD_BUG_ON(sizeof(*ptr) != sizeof(T_out)); \
+	BUILD_BUG_ON(offsetof(typeof(*ptr), member) != \
+		     offsetof(typeof(T_out), member)); \
+	BUILD_BUG_ON(sizeof(ptr->member) != sizeof(value)); \
+	BUILD_BUG_ON(sizeof(struct_member(T_out, member)) != sizeof(value)); \
+	BUILD_BUG_ON(!typecheck(typeof((ptr)->member), value));	\
+	(ptr)->member = (value); \
+	}

-:260: CHECK:MACRO_ARG_PRECEDENCE: Macro argument 'ptr' may be better as '(ptr)' to avoid precedence issues
#260: FILE: drivers/gpu/drm/xe/xe_eudebug.c:54:
+#define write_member(T_out, ptr, member, value) { \
+	BUILD_BUG_ON(sizeof(*ptr) != sizeof(T_out)); \
+	BUILD_BUG_ON(offsetof(typeof(*ptr), member) != \
+		     offsetof(typeof(T_out), member)); \
+	BUILD_BUG_ON(sizeof(ptr->member) != sizeof(value)); \
+	BUILD_BUG_ON(sizeof(struct_member(T_out, member)) != sizeof(value)); \
+	BUILD_BUG_ON(!typecheck(typeof((ptr)->member), value));	\
+	(ptr)->member = (value); \
+	}

-:260: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'member' - possible side-effects?
#260: FILE: drivers/gpu/drm/xe/xe_eudebug.c:54:
+#define write_member(T_out, ptr, member, value) { \
+	BUILD_BUG_ON(sizeof(*ptr) != sizeof(T_out)); \
+	BUILD_BUG_ON(offsetof(typeof(*ptr), member) != \
+		     offsetof(typeof(T_out), member)); \
+	BUILD_BUG_ON(sizeof(ptr->member) != sizeof(value)); \
+	BUILD_BUG_ON(sizeof(struct_member(T_out, member)) != sizeof(value)); \
+	BUILD_BUG_ON(!typecheck(typeof((ptr)->member), value));	\
+	(ptr)->member = (value); \
+	}

-:260: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'value' - possible side-effects?
#260: FILE: drivers/gpu/drm/xe/xe_eudebug.c:54:
+#define write_member(T_out, ptr, member, value) { \
+	BUILD_BUG_ON(sizeof(*ptr) != sizeof(T_out)); \
+	BUILD_BUG_ON(offsetof(typeof(*ptr), member) != \
+		     offsetof(typeof(T_out), member)); \
+	BUILD_BUG_ON(sizeof(ptr->member) != sizeof(value)); \
+	BUILD_BUG_ON(sizeof(struct_member(T_out, member)) != sizeof(value)); \
+	BUILD_BUG_ON(!typecheck(typeof((ptr)->member), value));	\
+	(ptr)->member = (value); \
+	}

-:532: CHECK:MACRO_ARG_REUSE: Macro argument reuse '_err' - possible side-effects?
#532: FILE: drivers/gpu/drm/xe/xe_eudebug.c:326:
+#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__); \
+	} \
+})

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

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

-:1597: WARNING:LONG_LINE: line length of 130 exceeds 100 columns
#1597: FILE: include/uapi/drm/xe_drm.h:121:
+#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, 13 checks, 1568 lines checked
c85f0d674e4c drm/xe/eudebug: Introduce discovery for resources
-:94: CHECK:LINE_SPACING: Please use a blank line after function/struct/union/enum declarations
#94: FILE: drivers/gpu/drm/xe/xe_device.h:218:
+}
+static inline void xe_eudebug_discovery_unlock(struct xe_device *xe, unsigned int cmd)

total: 0 errors, 0 warnings, 1 checks, 318 lines checked
31f53dce7293 drm/xe/eudebug: Introduce exec_queue events
3524200dba1a drm/xe/eudebug: hw enablement for eudebug
5606c41b22a2 drm/xe: Add EUDEBUG_ENABLE exec queue property
eab05032a20b drm/xe/eudebug: Introduce per device attention scan worker
-:390: CHECK:LINE_SPACING: Please don't use multiple blank lines
#390: FILE: drivers/gpu/drm/xe/xe_eudebug.c:1127:
+
+

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

total: 0 errors, 1 warnings, 1 checks, 735 lines checked
4b37794314ac drm/xe/eudebug: Introduce EU control interface
4aaf27fb08d6 drm/xe/eudebug: Add vm bind and vm bind ops
904c12faa332 drm/xe/eudebug: Add UFENCE events with acks
-:415: WARNING:LONG_LINE: line length of 114 exceeds 100 columns
#415: FILE: drivers/gpu/drm/xe/xe_eudebug.h:78:
+static inline void xe_eudebug_ufence_init(struct xe_user_fence *ufence, struct xe_file *xef, struct xe_vm *vm) { }

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

total: 0 errors, 1 warnings, 1 checks, 615 lines checked
d24f5ef48159 drm/xe/eudebug: vm open/pread/pwrite
-:116: CHECK:PARENTHESIS_ALIGNMENT: Alignment should match open parenthesis
#116: FILE: drivers/gpu/drm/xe/xe_eudebug.c:2960:
+static int xe_eudebug_bovma_access(struct xe_bo *bo, u64 offset,
+				    void *buf, u64 len, bool write)

total: 0 errors, 0 warnings, 1 checks, 570 lines checked
b6bda0e13538 drm/xe/eudebug: implement userptr_vma access
d85eae9bd2b1 drm/xe: Debug metadata create/destroy ioctls
-:34: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#34: 
new file mode 100644

-:201: CHECK:LINE_SPACING: Please don't use multiple blank lines
#201: FILE: drivers/gpu/drm/xe/xe_debug_metadata.h:49:
+
+

-:353: WARNING:LONG_LINE: line length of 143 exceeds 100 columns
#353: FILE: include/uapi/drm/xe_drm.h:123:
+#define DRM_IOCTL_XE_DEBUG_METADATA_CREATE	 DRM_IOWR(DRM_COMMAND_BASE + DRM_XE_DEBUG_METADATA_CREATE, struct drm_xe_debug_metadata_create)

-:354: WARNING:LONG_LINE: line length of 144 exceeds 100 columns
#354: FILE: include/uapi/drm/xe_drm.h:124:
+#define DRM_IOCTL_XE_DEBUG_METADATA_DESTROY	 DRM_IOW(DRM_COMMAND_BASE + DRM_XE_DEBUG_METADATA_DESTROY, struct drm_xe_debug_metadata_destroy)

total: 0 errors, 3 warnings, 1 checks, 341 lines checked
1e7e3d39885d drm/xe: Attach debug metadata to vma
-:6: WARNING:COMMIT_LOG_LONG_LINE: Prefer a maximum 75 chars per line (possible unwrapped commit description?)
#6: 
Introduces a vm_bind_op extension, enabling users to attach metadata objects

-:207: WARNING:LONG_LINE: line length of 107 exceeds 100 columns
#207: FILE: drivers/gpu/drm/xe/xe_debug_metadata.h:55:
+static inline struct xe_debug_metadata *xe_debug_metadata_get(struct xe_file *xef, u32 id) { return NULL; }

-:312: WARNING:LONG_LINE: line length of 109 exceeds 100 columns
#312: FILE: drivers/gpu/drm/xe/xe_vm.c:2259:
+					err = xe_eudebug_copy_vma_metadata(&op->remap.prev->eudebug.metadata,

total: 0 errors, 3 warnings, 0 checks, 482 lines checked
8765e631476b drm/xe/eudebug: Add debug metadata support for xe_eudebug
-:527: WARNING:LONG_LINE: line length of 122 exceeds 100 columns
#527: FILE: drivers/gpu/drm/xe/xe_eudebug.h:80:
+static inline void xe_eudebug_vm_bind_op_add(struct xe_vm *vm, u32 op, u64 addr, u64 range, struct drm_gpuva_ops *ops) { }

-:535: WARNING:LONG_LINE: line length of 105 exceeds 100 columns
#535: FILE: drivers/gpu/drm/xe/xe_eudebug.h:90:
+static inline void xe_eudebug_debug_metadata_create(struct xe_file *xef, struct xe_debug_metadata *m) { }

-:536: WARNING:LONG_LINE: line length of 106 exceeds 100 columns
#536: FILE: drivers/gpu/drm/xe/xe_eudebug.h:91:
+static inline void xe_eudebug_debug_metadata_destroy(struct xe_file *xef, struct xe_debug_metadata *m) { }

total: 0 errors, 3 warnings, 0 checks, 580 lines checked
6de2c5528aa1 drm/xe/eudebug: Implement vm_bind_op discovery
a3740d54ebdd drm/xe/eudebug: Dynamically toggle debugger functionality
fefc940eb695 drm/xe/eudebug_test: Introduce xe_eudebug wa kunit test
-:15: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#15: 
new file mode 100644

total: 0 errors, 1 warnings, 0 checks, 193 lines checked



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

* ✗ CI.KUnit: failure for GPU debug support (eudebug) (rev2)
  2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
                   ` (19 preceding siblings ...)
  2024-10-01 15:03 ` ✗ CI.checkpatch: warning " Patchwork
@ 2024-10-01 15:03 ` Patchwork
  20 siblings, 0 replies; 40+ messages in thread
From: Patchwork @ 2024-10-01 15:03 UTC (permalink / raw)
  To: Mika Kuoppala; +Cc: intel-xe

== Series Details ==

Series: GPU debug support (eudebug) (rev2)
URL   : https://patchwork.freedesktop.org/series/136572/
State : failure

== Summary ==

+ trap cleanup EXIT
+ /kernel/tools/testing/kunit/kunit.py run --kunitconfig /kernel/drivers/gpu/drm/xe/.kunitconfig
ERROR:root:../lib/iomap.c:156:5: warning: no previous prototype for ‘ioread64_lo_hi’ [-Wmissing-prototypes]
  156 | u64 ioread64_lo_hi(const void __iomem *addr)
      |     ^~~~~~~~~~~~~~
../lib/iomap.c:163:5: warning: no previous prototype for ‘ioread64_hi_lo’ [-Wmissing-prototypes]
  163 | u64 ioread64_hi_lo(const void __iomem *addr)
      |     ^~~~~~~~~~~~~~
../lib/iomap.c:170:5: warning: no previous prototype for ‘ioread64be_lo_hi’ [-Wmissing-prototypes]
  170 | u64 ioread64be_lo_hi(const void __iomem *addr)
      |     ^~~~~~~~~~~~~~~~
../lib/iomap.c:178:5: warning: no previous prototype for ‘ioread64be_hi_lo’ [-Wmissing-prototypes]
  178 | u64 ioread64be_hi_lo(const void __iomem *addr)
      |     ^~~~~~~~~~~~~~~~
../lib/iomap.c:264:6: warning: no previous prototype for ‘iowrite64_lo_hi’ [-Wmissing-prototypes]
  264 | void iowrite64_lo_hi(u64 val, void __iomem *addr)
      |      ^~~~~~~~~~~~~~~
../lib/iomap.c:272:6: warning: no previous prototype for ‘iowrite64_hi_lo’ [-Wmissing-prototypes]
  272 | void iowrite64_hi_lo(u64 val, void __iomem *addr)
      |      ^~~~~~~~~~~~~~~
../lib/iomap.c:280:6: warning: no previous prototype for ‘iowrite64be_lo_hi’ [-Wmissing-prototypes]
  280 | void iowrite64be_lo_hi(u64 val, void __iomem *addr)
      |      ^~~~~~~~~~~~~~~~~
../lib/iomap.c:288:6: warning: no previous prototype for ‘iowrite64be_hi_lo’ [-Wmissing-prototypes]
  288 | void iowrite64be_hi_lo(u64 val, void __iomem *addr)
      |      ^~~~~~~~~~~~~~~~~
../drivers/gpu/drm/xe/xe_eudebug.c:1357:27: error: ‘no_llseek’ undeclared here (not in a function); did you mean ‘noop_llseek’?
 1357 |         .llseek         = no_llseek,
      |                           ^~~~~~~~~
      |                           noop_llseek
make[7]: *** [../scripts/Makefile.build:229: drivers/gpu/drm/xe/xe_eudebug.o] Error 1
make[7]: *** Waiting for unfinished jobs....
make[6]: *** [../scripts/Makefile.build:478: drivers/gpu/drm/xe] Error 2
make[5]: *** [../scripts/Makefile.build:478: drivers/gpu/drm] Error 2
make[4]: *** [../scripts/Makefile.build:478: drivers/gpu] Error 2
make[3]: *** [../scripts/Makefile.build:478: drivers] Error 2
make[2]: *** [/kernel/Makefile:1936: .] Error 2
make[1]: *** [/kernel/Makefile:224: __sub-make] Error 2
make: *** [Makefile:224: __sub-make] Error 2

[15:03:19] Configuring KUnit Kernel ...
Generating .config ...
Populating config with:
$ make ARCH=um O=.kunit olddefconfig
[15:03:24] Building KUnit Kernel ...
Populating config with:
$ make ARCH=um O=.kunit olddefconfig
Building with:
$ make all compile_commands.json ARCH=um O=.kunit --jobs=48
+ cleanup
++ stat -c %u:%g /kernel
+ chown -R 1003:1003 /kernel



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

* Re: [PATCH 13/18] drm/xe: Debug metadata create/destroy ioctls
  2024-10-01 14:43 ` [PATCH 13/18] drm/xe: Debug metadata create/destroy ioctls Mika Kuoppala
@ 2024-10-01 16:25   ` Matthew Auld
  2024-10-02 13:59     ` Grzegorzek, Dominik
  0 siblings, 1 reply; 40+ messages in thread
From: Matthew Auld @ 2024-10-01 16:25 UTC (permalink / raw)
  To: Mika Kuoppala, intel-xe; +Cc: Dominik Grzegorzek

Hi,

On 01/10/2024 15:43, Mika Kuoppala wrote:
> From: Dominik Grzegorzek <dominik.grzegorzek@intel.com>
> 
> Ad a part of eu debug feature introduce debug metadata objects.
> These are to be used to pass metadata between client and debugger,
> by attaching them to vm_bind operations.
> 
> todo: WORK_IN_PROGRESS_* defines need to be reworded/refined when
>        the real usage and need is established by l0+gdb.
> 
> v2: - include uapi/drm/xe_drm.h
>      - metadata behind kconfig (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                  |   3 +-
>   drivers/gpu/drm/xe/xe_debug_metadata.c       | 108 +++++++++++++++++++
>   drivers/gpu/drm/xe/xe_debug_metadata.h       |  50 +++++++++
>   drivers/gpu/drm/xe/xe_debug_metadata_types.h |  28 +++++
>   drivers/gpu/drm/xe/xe_device.c               |   5 +
>   drivers/gpu/drm/xe/xe_device.h               |   2 +
>   drivers/gpu/drm/xe/xe_device_types.h         |   7 ++
>   drivers/gpu/drm/xe/xe_eudebug.c              |  13 +++
>   include/uapi/drm/xe_drm.h                    |  53 ++++++++-
>   9 files changed, 267 insertions(+), 2 deletions(-)
>   create mode 100644 drivers/gpu/drm/xe/xe_debug_metadata.c
>   create mode 100644 drivers/gpu/drm/xe/xe_debug_metadata.h
>   create mode 100644 drivers/gpu/drm/xe/xe_debug_metadata_types.h
> 
> diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
> index 660c1dbba8d0..a94f771529a4 100644
> --- a/drivers/gpu/drm/xe/Makefile
> +++ b/drivers/gpu/drm/xe/Makefile
> @@ -114,7 +114,8 @@ xe-y += xe_bb.o \
>   	xe_wa.o \
>   	xe_wopcm.o
>   
> -xe-$(CONFIG_DRM_XE_EUDEBUG) += xe_eudebug.o
> +xe-$(CONFIG_DRM_XE_EUDEBUG) += xe_eudebug.o \
> +	xe_debug_metadata.o
>   
>   xe-$(CONFIG_HMM_MIRROR) += xe_hmm.o
>   
> diff --git a/drivers/gpu/drm/xe/xe_debug_metadata.c b/drivers/gpu/drm/xe/xe_debug_metadata.c
> new file mode 100644
> index 000000000000..72a00b628475
> --- /dev/null
> +++ b/drivers/gpu/drm/xe/xe_debug_metadata.c
> @@ -0,0 +1,108 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2023 Intel Corporation
> + */
> +#include "xe_debug_metadata.h"
> +
> +#include <drm/drm_device.h>
> +#include <drm/drm_file.h>
> +#include <uapi/drm/xe_drm.h>
> +
> +#include "xe_device.h"
> +#include "xe_macros.h"
> +
> +static void xe_debug_metadata_release(struct kref *ref)
> +{
> +	struct xe_debug_metadata *mdata = container_of(ref, struct xe_debug_metadata, refcount);
> +
> +	kvfree(mdata->ptr);
> +	kfree(mdata);
> +}
> +
> +void xe_debug_metadata_put(struct xe_debug_metadata *mdata)
> +{
> +	kref_put(&mdata->refcount, xe_debug_metadata_release);
> +}
> +
> +int xe_debug_metadata_create_ioctl(struct drm_device *dev,
> +				   void *data,
> +				   struct drm_file *file)
> +{
> +	struct xe_device *xe = to_xe_device(dev);
> +	struct xe_file *xef = to_xe_file(file);
> +	struct drm_xe_debug_metadata_create *args = data;
> +	struct xe_debug_metadata *mdata;
> +	int err;
> +	u32 id;
> +
> +	if (XE_IOCTL_DBG(xe, args->extensions))
> +		return -EINVAL;
> +
> +	if (XE_IOCTL_DBG(xe, args->type > DRM_XE_DEBUG_METADATA_PROGRAM_MODULE))
> +		return -EINVAL;
> +
> +	if (XE_IOCTL_DBG(xe, !args->user_addr || !args->len))
> +		return -EINVAL;
> +
> +	if (XE_IOCTL_DBG(xe, !access_ok(u64_to_user_ptr(args->user_addr), args->len)))
> +		return -EFAULT;
> +
> +	mdata = kzalloc(sizeof(*mdata), GFP_KERNEL);
> +	if (!mdata)
> +		return -ENOMEM;
> +
> +	mdata->len = args->len;
> +	mdata->type = args->type;
> +
> +	mdata->ptr = kvmalloc(mdata->len, GFP_KERNEL);
> +	if (!mdata->ptr) {
> +		kfree(mdata);
> +		return -ENOMEM;
> +	}
> +	kref_init(&mdata->refcount);
> +
> +	err = copy_from_user(mdata->ptr, u64_to_user_ptr(args->user_addr), mdata->len);
> +	if (err) {
> +		err = -EFAULT;
> +		goto put_mdata;
> +	}
> +
> +	mutex_lock(&xef->eudebug.metadata.lock);
> +	err = xa_alloc(&xef->eudebug.metadata.xa, &id, mdata, xa_limit_32b, GFP_KERNEL);
> +	mutex_unlock(&xef->eudebug.metadata.lock);
> +
> +	args->metadata_id = id;

Just some drive-by-comments. Potentially this is passing uninitialised 
data from the stack back to userspace, assuming the xa_alloc here fails? 
Maybe safer to leave metadata_id zeroed on err?

> +	mdata->id = id;

It looks like mdata can go out of scope since user can technically guess 
the id and call the destroy ioctl before this ioctl returns. I think 
publishing the id needs to go last or needs some kind of locking.

Also are we sure we need to store the user id in mdata in the first 
place? Usually for user id kmd doesn't need to track it in such a way.

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

* Re: [PATCH 13/18] drm/xe: Debug metadata create/destroy ioctls
  2024-10-01 16:25   ` Matthew Auld
@ 2024-10-02 13:59     ` Grzegorzek, Dominik
  0 siblings, 0 replies; 40+ messages in thread
From: Grzegorzek, Dominik @ 2024-10-02 13:59 UTC (permalink / raw)
  To: mika.kuoppala@linux.intel.com, intel-xe@lists.freedesktop.org,
	Auld, Matthew

On Tue, 2024-10-01 at 17:25 +0100, Matthew Auld wrote:
> Hi,
> 
> On 01/10/2024 15:43, Mika Kuoppala wrote:
> > From: Dominik Grzegorzek <dominik.grzegorzek@intel.com>
> > 
> > Ad a part of eu debug feature introduce debug metadata objects.
> > These are to be used to pass metadata between client and debugger,
> > by attaching them to vm_bind operations.
> > 
> > todo: WORK_IN_PROGRESS_* defines need to be reworded/refined when
> >        the real usage and need is established by l0+gdb.
> > 
> > v2: - include uapi/drm/xe_drm.h
> >      - metadata behind kconfig (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                  |   3 +-
> >   drivers/gpu/drm/xe/xe_debug_metadata.c       | 108 +++++++++++++++++++
> >   drivers/gpu/drm/xe/xe_debug_metadata.h       |  50 +++++++++
> >   drivers/gpu/drm/xe/xe_debug_metadata_types.h |  28 +++++
> >   drivers/gpu/drm/xe/xe_device.c               |   5 +
> >   drivers/gpu/drm/xe/xe_device.h               |   2 +
> >   drivers/gpu/drm/xe/xe_device_types.h         |   7 ++
> >   drivers/gpu/drm/xe/xe_eudebug.c              |  13 +++
> >   include/uapi/drm/xe_drm.h                    |  53 ++++++++-
> >   9 files changed, 267 insertions(+), 2 deletions(-)
> >   create mode 100644 drivers/gpu/drm/xe/xe_debug_metadata.c
> >   create mode 100644 drivers/gpu/drm/xe/xe_debug_metadata.h
> >   create mode 100644 drivers/gpu/drm/xe/xe_debug_metadata_types.h
> > 
> > diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
> > index 660c1dbba8d0..a94f771529a4 100644
> > --- a/drivers/gpu/drm/xe/Makefile
> > +++ b/drivers/gpu/drm/xe/Makefile
> > @@ -114,7 +114,8 @@ xe-y += xe_bb.o \
> >   	xe_wa.o \
> >   	xe_wopcm.o
> >   
> > -xe-$(CONFIG_DRM_XE_EUDEBUG) += xe_eudebug.o
> > +xe-$(CONFIG_DRM_XE_EUDEBUG) += xe_eudebug.o \
> > +	xe_debug_metadata.o
> >   
> >   xe-$(CONFIG_HMM_MIRROR) += xe_hmm.o
> >   
> > diff --git a/drivers/gpu/drm/xe/xe_debug_metadata.c b/drivers/gpu/drm/xe/xe_debug_metadata.c
> > new file mode 100644
> > index 000000000000..72a00b628475
> > --- /dev/null
> > +++ b/drivers/gpu/drm/xe/xe_debug_metadata.c
> > @@ -0,0 +1,108 @@
> > +// SPDX-License-Identifier: MIT
> > +/*
> > + * Copyright © 2023 Intel Corporation
> > + */
> > +#include "xe_debug_metadata.h"
> > +
> > +#include <drm/drm_device.h>
> > +#include <drm/drm_file.h>
> > +#include <uapi/drm/xe_drm.h>
> > +
> > +#include "xe_device.h"
> > +#include "xe_macros.h"
> > +
> > +static void xe_debug_metadata_release(struct kref *ref)
> > +{
> > +	struct xe_debug_metadata *mdata = container_of(ref, struct xe_debug_metadata, refcount);
> > +
> > +	kvfree(mdata->ptr);
> > +	kfree(mdata);
> > +}
> > +
> > +void xe_debug_metadata_put(struct xe_debug_metadata *mdata)
> > +{
> > +	kref_put(&mdata->refcount, xe_debug_metadata_release);
> > +}
> > +
> > +int xe_debug_metadata_create_ioctl(struct drm_device *dev,
> > +				   void *data,
> > +				   struct drm_file *file)
> > +{
> > +	struct xe_device *xe = to_xe_device(dev);
> > +	struct xe_file *xef = to_xe_file(file);
> > +	struct drm_xe_debug_metadata_create *args = data;
> > +	struct xe_debug_metadata *mdata;
> > +	int err;
> > +	u32 id;
> > +
> > +	if (XE_IOCTL_DBG(xe, args->extensions))
> > +		return -EINVAL;
> > +
> > +	if (XE_IOCTL_DBG(xe, args->type > DRM_XE_DEBUG_METADATA_PROGRAM_MODULE))
> > +		return -EINVAL;
> > +
> > +	if (XE_IOCTL_DBG(xe, !args->user_addr || !args->len))
> > +		return -EINVAL;
> > +
> > +	if (XE_IOCTL_DBG(xe, !access_ok(u64_to_user_ptr(args->user_addr), args->len)))
> > +		return -EFAULT;
> > +
> > +	mdata = kzalloc(sizeof(*mdata), GFP_KERNEL);
> > +	if (!mdata)
> > +		return -ENOMEM;
> > +
> > +	mdata->len = args->len;
> > +	mdata->type = args->type;
> > +
> > +	mdata->ptr = kvmalloc(mdata->len, GFP_KERNEL);
> > +	if (!mdata->ptr) {
> > +		kfree(mdata);
> > +		return -ENOMEM;
> > +	}
> > +	kref_init(&mdata->refcount);
> > +
> > +	err = copy_from_user(mdata->ptr, u64_to_user_ptr(args->user_addr), mdata->len);
> > +	if (err) {
> > +		err = -EFAULT;
> > +		goto put_mdata;
> > +	}
> > +
> > +	mutex_lock(&xef->eudebug.metadata.lock);
> > +	err = xa_alloc(&xef->eudebug.metadata.xa, &id, mdata, xa_limit_32b, GFP_KERNEL);
> > +	mutex_unlock(&xef->eudebug.metadata.lock);
> > +
> > +	args->metadata_id = id;
> 
> Just some drive-by-comments. Potentially this is passing uninitialised 
> data from the stack back to userspace, assuming the xa_alloc here fails? 
> Maybe safer to leave metadata_id zeroed on err?

That's mine over oversight. Thanks for catching that!
> 
> > +	mdata->id = id;
> 
> It looks like mdata can go out of scope since user can technically guess 
> the id and call the destroy ioctl before this ioctl returns. I think 
> publishing the id needs to go last or needs some kind of locking.
> 
> Also are we sure we need to store the user id in mdata in the first 
> place? Usually for user id kmd doesn't need to track it in such a way.

I think we can get rid of it fairly easy, I will to that in next revision. 

Regards,
Dominik


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

* Re: [PATCH 03/18] drm/xe/eudebug: Introduce discovery for resources
  2024-10-01 14:42 ` [PATCH 03/18] drm/xe/eudebug: Introduce discovery for resources Mika Kuoppala
@ 2024-10-03  0:41   ` Matthew Brost
  2024-10-03  7:27     ` Mika Kuoppala
  0 siblings, 1 reply; 40+ messages in thread
From: Matthew Brost @ 2024-10-03  0:41 UTC (permalink / raw)
  To: Mika Kuoppala; +Cc: intel-xe, Dominik Grzegorzek, Maciej Patelczyk

On Tue, Oct 01, 2024 at 05:42:51PM +0300, Mika Kuoppala wrote:
> Debugger connection can happen way after the client has
> created and destroyed arbitrary number of resources.
> 
> We need to playback all currently existing resources for the
> debugger. The client is held until this so called discovery
> process, executed by workqueue, is complete.
> 
> This patch is based on discovery work by Maciej Patelczyk
> for i915 driver.
> 
> v2: - use rw_semaphore to block drm_ioctls during discovery (Matthew)
>     - only lock according to ioctl at play (Dominik)
> 
> 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>
> ---
>  drivers/gpu/drm/xe/xe_device.c        |  10 +-
>  drivers/gpu/drm/xe/xe_device.h        |  34 +++++++
>  drivers/gpu/drm/xe/xe_device_types.h  |   6 ++
>  drivers/gpu/drm/xe/xe_eudebug.c       | 135 +++++++++++++++++++++++++-
>  drivers/gpu/drm/xe/xe_eudebug_types.h |   7 ++
>  5 files changed, 185 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
> index 5615e2c23bf6..ec5eedbbf320 100644
> --- a/drivers/gpu/drm/xe/xe_device.c
> +++ b/drivers/gpu/drm/xe/xe_device.c
> @@ -215,8 +215,11 @@ static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>  		return -ECANCELED;
>  
>  	ret = xe_pm_runtime_get_ioctl(xe);
> -	if (ret >= 0)
> +	if (ret >= 0) {
> +		xe_eudebug_discovery_lock(xe, cmd);
>  		ret = drm_ioctl(file, cmd, arg);
> +		xe_eudebug_discovery_unlock(xe, cmd);
> +	}
>  	xe_pm_runtime_put(xe);
>  
>  	return ret;
> @@ -233,8 +236,11 @@ static long xe_drm_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo
>  		return -ECANCELED;
>  
>  	ret = xe_pm_runtime_get_ioctl(xe);
> -	if (ret >= 0)
> +	if (ret >= 0) {
> +		xe_eudebug_discovery_lock(xe, cmd);
>  		ret = drm_compat_ioctl(file, cmd, arg);
> +		xe_eudebug_discovery_unlock(xe, cmd);
> +	}
>  	xe_pm_runtime_put(xe);
>  
>  	return ret;
> diff --git a/drivers/gpu/drm/xe/xe_device.h b/drivers/gpu/drm/xe/xe_device.h
> index 4c3f0ebe78a9..b2fc85b1d26e 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"
> @@ -191,4 +192,37 @@ void xe_device_declare_wedged(struct xe_device *xe);
>  struct xe_file *xe_file_get(struct xe_file *xef);
>  void xe_file_put(struct xe_file *xef);
>  
> +#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
> +static inline int xe_eudebug_needs_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:
> +	case DRM_XE_EUDEBUG_CONNECT:
> +		return 1;
> +	}
> +
> +	return 0;
> +}
> +
> +static inline void xe_eudebug_discovery_lock(struct xe_device *xe, unsigned int cmd)
> +{
> +	if (xe_eudebug_needs_lock(cmd))
> +		down_read(&xe->eudebug.discovery_lock);
> +}
> +static inline void xe_eudebug_discovery_unlock(struct xe_device *xe, unsigned int cmd)
> +{
> +	if (xe_eudebug_needs_lock(cmd))
> +		up_read(&xe->eudebug.discovery_lock);
> +}
> +#else
> +static inline void xe_eudebug_discovery_lock(struct xe_device *xe, unsigned int cmd) { }
> +static inline void xe_eudebug_discovery_unlock(struct xe_device *xe, 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 cb4b52888a4b..54ceeee7cf75 100644
> --- a/drivers/gpu/drm/xe/xe_device_types.h
> +++ b/drivers/gpu/drm/xe/xe_device_types.h
> @@ -542,6 +542,12 @@ struct xe_device {
>  
>  		/** @available: is the debugging functionality available */
>  		bool available;
> +
> +		/** @ordered_wq: used to discovery */
> +		struct workqueue_struct *ordered_wq;
> +
> +		/** discovery_lock: used for discovery to block xe ioctls */
> +		struct rw_semaphore discovery_lock;
>  	} eudebug;
>  #endif
>  
> diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
> index ea0cfd7697aa..c294e2c6152b 100644
> --- a/drivers/gpu/drm/xe/xe_eudebug.c
> +++ b/drivers/gpu/drm/xe/xe_eudebug.c
> @@ -299,6 +299,8 @@ static bool xe_eudebug_detach(struct xe_device *xe,
>  	}
>  	spin_unlock(&d->connection.lock);
>  
> +	flush_work(&d->discovery_work);
> +
>  	if (!detached)
>  		return false;
>  
> @@ -409,7 +411,7 @@ static struct task_struct *find_task_get(struct xe_file *xef)
>  }
>  
>  static struct xe_eudebug *
> -xe_eudebug_get(struct xe_file *xef)
> +_xe_eudebug_get(struct xe_file *xef)
>  {
>  	struct task_struct *task;
>  	struct xe_eudebug *d;
> @@ -433,6 +435,24 @@ xe_eudebug_get(struct xe_file *xef)
>  	return d;
>  }
>  
> +static struct xe_eudebug *
> +xe_eudebug_get(struct xe_file *xef)
> +{
> +	struct xe_eudebug *d;
> +
> +	lockdep_assert_held(&xef->xe->eudebug.discovery_lock);
> +
> +	d = _xe_eudebug_get(xef);
> +	if (d) {
> +		if (!completion_done(&d->discovery)) {
> +			xe_eudebug_put(d);
> +			d = NULL;
> +		}
> +	}
> +
> +	return d;
> +}
> +
>  static int xe_eudebug_queue_event(struct xe_eudebug *d,
>  				  struct xe_eudebug_event *event)
>  {
> @@ -810,6 +830,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,
> @@ -832,6 +856,8 @@ static const struct file_operations fops = {
>  	.unlocked_ioctl	= xe_eudebug_ioctl,
>  };
>  
> +static void discovery_work_fn(struct work_struct *work);
> +
>  static int
>  xe_eudebug_connect(struct xe_device *xe,
>  		   struct drm_xe_eudebug_connect *param)
> @@ -866,9 +892,11 @@ xe_eudebug_connect(struct xe_device *xe,
>  	spin_lock_init(&d->connection.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);
>  
>  	d->res = xe_eudebug_resources_alloc();
>  	if (IS_ERR(d->res)) {
> @@ -886,6 +914,9 @@ xe_eudebug_connect(struct xe_device *xe,
>  		goto err_detach;
>  	}
>  
> +	kref_get(&d->ref);
> +	queue_work(xe->eudebug.ordered_wq, &d->discovery_work);
> +
>  	eu_dbg(d, "connected session %lld", d->session);
>  
>  	return fd;
> @@ -918,13 +949,18 @@ void xe_eudebug_init(struct xe_device *xe)
>  	spin_lock_init(&xe->eudebug.lock);
>  	INIT_LIST_HEAD(&xe->eudebug.list);
>  	INIT_LIST_HEAD(&xe->clients.list);
> +	init_rwsem(&xe->eudebug.discovery_lock);
>  
> -	xe->eudebug.available = true;
> +	xe->eudebug.ordered_wq = alloc_ordered_workqueue("xe-eudebug-ordered-wq", 0);
> +	xe->eudebug.available = !!xe->eudebug.ordered_wq;
>  }
>  
>  void xe_eudebug_fini(struct xe_device *xe)
>  {
>  	xe_assert(xe, list_empty_careful(&xe->eudebug.list));
> +
> +	if (xe->eudebug.ordered_wq)
> +		destroy_workqueue(xe->eudebug.ordered_wq);
>  }
>  
>  static int send_open_event(struct xe_eudebug *d, u32 flags, const u64 handle,
> @@ -990,21 +1026,25 @@ void xe_eudebug_file_open(struct xe_file *xef)
>  	struct xe_eudebug *d;
>  
>  	INIT_LIST_HEAD(&xef->eudebug.client_link);
> +
> +	down_read(&xef->xe->eudebug.discovery_lock);
> +
>  	spin_lock(&xef->xe->clients.lock);
>  	list_add_tail(&xef->eudebug.client_link, &xef->xe->clients.list);
>  	spin_unlock(&xef->xe->clients.lock);
>  
>  	d = xe_eudebug_get(xef);

This looks like this could deadlock.

- discovery_work_fn is queued with &d->discovery not complete
- This function runs and grabs &xef->xe->eudebug.discovery_lock in read
  mode
- completion_done(&d->discovery) is waited on xe_eudebug_get
- discovery_work_fn can't complete d->discovery) on
  &xef->xe->eudebug.discovery_lock in write mode

The summary is - it not safe to wait on '&d->discovery' while holding
&xef->xe->eudebug.discovery_lock.

Matt

> -	if (!d)
> -		return;
> +	if (d)
> +		xe_eudebug_event_put(d, client_create_event(d, xef));
>  
> -	xe_eudebug_event_put(d, client_create_event(d, xef));
> +	up_read(&xef->xe->eudebug.discovery_lock);
>  }
>  
>  void xe_eudebug_file_close(struct xe_file *xef)
>  {
>  	struct xe_eudebug *d;
>  
> +	down_read(&xef->xe->eudebug.discovery_lock);
>  	d = xe_eudebug_get(xef);
>  	if (d)
>  		xe_eudebug_event_put(d, client_destroy_event(d, xef));
> @@ -1012,6 +1052,8 @@ void xe_eudebug_file_close(struct xe_file *xef)
>  	spin_lock(&xef->xe->clients.lock);
>  	list_del_init(&xef->eudebug.client_link);
>  	spin_unlock(&xef->xe->clients.lock);
> +
> +	up_read(&xef->xe->eudebug.discovery_lock);
>  }
>  
>  static int send_vm_event(struct xe_eudebug *d, u32 flags,
> @@ -1112,3 +1154,86 @@ 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 int discover_client(struct xe_eudebug *d, struct xe_file *xef)
> +{
> +	struct xe_vm *vm;
> +	unsigned long i;
> +	int err;
> +
> +	err = client_create_event(d, xef);
> +	if (err)
> +		return err;
> +
> +	xa_for_each(&xef->vm.xa, i, vm) {
> +		err = vm_create_event(d, xef, vm);
> +		if (err)
> +			break;
> +	}
> +
> +	return err;
> +}
> +
> +static bool xe_eudebug_task_match(struct xe_eudebug *d, struct xe_file *xef)
> +{
> +	struct task_struct *task;
> +	bool match;
> +
> +	task = find_task_get(xef);
> +	if (!task)
> +		return false;
> +
> +	match = same_thread_group(d->target_task, task);
> +
> +	put_task_struct(task);
> +
> +	return match;
> +}
> +
> +static void discover_clients(struct xe_device *xe, struct xe_eudebug *d)
> +{
> +	struct xe_file *xef;
> +	int err;
> +
> +	list_for_each_entry(xef, &xe->clients.list, eudebug.client_link) {
> +		if (xe_eudebug_detached(d))
> +			break;
> +
> +		if (xe_eudebug_task_match(d, xef))
> +			err = discover_client(d, xef);
> +		else
> +			err = 0;
> +
> +		if (err) {
> +			eu_dbg(d, "discover client %p: %d\n", xef, err);
> +			break;
> +		}
> +	}
> +}
> +
> +static void discovery_work_fn(struct work_struct *work)
> +{
> +	struct xe_eudebug *d = container_of(work, typeof(*d),
> +					    discovery_work);
> +	struct xe_device *xe = d->xe;
> +
> +	if (xe_eudebug_detached(d)) {
> +		complete_all(&d->discovery);
> +		xe_eudebug_put(d);
> +		return;
> +	}
> +
> +	down_write(&xe->eudebug.discovery_lock);
> +
> +	eu_dbg(d, "Discovery start for %lld\n", d->session);
> +
> +	discover_clients(xe, d);
> +
> +	eu_dbg(d, "Discovery end for %lld\n", d->session);
> +
> +	complete_all(&d->discovery);
> +
> +	up_write(&xe->eudebug.discovery_lock);
> +
> +	xe_eudebug_put(d);
> +}
> diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
> index a5185f18f640..080a821db3e4 100644
> --- a/drivers/gpu/drm/xe/xe_eudebug_types.h
> +++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
> @@ -19,6 +19,7 @@
>  struct xe_device;
>  struct task_struct;
>  struct xe_eudebug_event;
> +struct workqueue_struct;
>  
>  #define CONFIG_DRM_XE_DEBUGGER_EVENT_QUEUE_SIZE 64
>  
> @@ -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.34.1
> 

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

* Re: [PATCH 03/18] drm/xe/eudebug: Introduce discovery for resources
  2024-10-03  0:41   ` Matthew Brost
@ 2024-10-03  7:27     ` Mika Kuoppala
  2024-10-03 16:32       ` Matthew Brost
  0 siblings, 1 reply; 40+ messages in thread
From: Mika Kuoppala @ 2024-10-03  7:27 UTC (permalink / raw)
  To: Matthew Brost; +Cc: intel-xe, Dominik Grzegorzek, Maciej Patelczyk

Matthew Brost <matthew.brost@intel.com> writes:

> On Tue, Oct 01, 2024 at 05:42:51PM +0300, Mika Kuoppala wrote:
>> Debugger connection can happen way after the client has
>> created and destroyed arbitrary number of resources.
>> 
>> We need to playback all currently existing resources for the
>> debugger. The client is held until this so called discovery
>> process, executed by workqueue, is complete.
>> 
>> This patch is based on discovery work by Maciej Patelczyk
>> for i915 driver.
>> 
>> v2: - use rw_semaphore to block drm_ioctls during discovery (Matthew)
>>     - only lock according to ioctl at play (Dominik)
>> 
>> 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>
>> ---
>>  drivers/gpu/drm/xe/xe_device.c        |  10 +-
>>  drivers/gpu/drm/xe/xe_device.h        |  34 +++++++
>>  drivers/gpu/drm/xe/xe_device_types.h  |   6 ++
>>  drivers/gpu/drm/xe/xe_eudebug.c       | 135 +++++++++++++++++++++++++-
>>  drivers/gpu/drm/xe/xe_eudebug_types.h |   7 ++
>>  5 files changed, 185 insertions(+), 7 deletions(-)
>> 
>> diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
>> index 5615e2c23bf6..ec5eedbbf320 100644
>> --- a/drivers/gpu/drm/xe/xe_device.c
>> +++ b/drivers/gpu/drm/xe/xe_device.c
>> @@ -215,8 +215,11 @@ static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>>  		return -ECANCELED;
>>  
>>  	ret = xe_pm_runtime_get_ioctl(xe);
>> -	if (ret >= 0)
>> +	if (ret >= 0) {
>> +		xe_eudebug_discovery_lock(xe, cmd);
>>  		ret = drm_ioctl(file, cmd, arg);
>> +		xe_eudebug_discovery_unlock(xe, cmd);
>> +	}
>>  	xe_pm_runtime_put(xe);
>>  
>>  	return ret;
>> @@ -233,8 +236,11 @@ static long xe_drm_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo
>>  		return -ECANCELED;
>>  
>>  	ret = xe_pm_runtime_get_ioctl(xe);
>> -	if (ret >= 0)
>> +	if (ret >= 0) {
>> +		xe_eudebug_discovery_lock(xe, cmd);
>>  		ret = drm_compat_ioctl(file, cmd, arg);
>> +		xe_eudebug_discovery_unlock(xe, cmd);
>> +	}
>>  	xe_pm_runtime_put(xe);
>>  
>>  	return ret;
>> diff --git a/drivers/gpu/drm/xe/xe_device.h b/drivers/gpu/drm/xe/xe_device.h
>> index 4c3f0ebe78a9..b2fc85b1d26e 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"
>> @@ -191,4 +192,37 @@ void xe_device_declare_wedged(struct xe_device *xe);
>>  struct xe_file *xe_file_get(struct xe_file *xef);
>>  void xe_file_put(struct xe_file *xef);
>>  
>> +#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
>> +static inline int xe_eudebug_needs_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:
>> +	case DRM_XE_EUDEBUG_CONNECT:
>> +		return 1;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static inline void xe_eudebug_discovery_lock(struct xe_device *xe, unsigned int cmd)
>> +{
>> +	if (xe_eudebug_needs_lock(cmd))
>> +		down_read(&xe->eudebug.discovery_lock);
>> +}
>> +static inline void xe_eudebug_discovery_unlock(struct xe_device *xe, unsigned int cmd)
>> +{
>> +	if (xe_eudebug_needs_lock(cmd))
>> +		up_read(&xe->eudebug.discovery_lock);
>> +}
>> +#else
>> +static inline void xe_eudebug_discovery_lock(struct xe_device *xe, unsigned int cmd) { }
>> +static inline void xe_eudebug_discovery_unlock(struct xe_device *xe, 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 cb4b52888a4b..54ceeee7cf75 100644
>> --- a/drivers/gpu/drm/xe/xe_device_types.h
>> +++ b/drivers/gpu/drm/xe/xe_device_types.h
>> @@ -542,6 +542,12 @@ struct xe_device {
>>  
>>  		/** @available: is the debugging functionality available */
>>  		bool available;
>> +
>> +		/** @ordered_wq: used to discovery */
>> +		struct workqueue_struct *ordered_wq;
>> +
>> +		/** discovery_lock: used for discovery to block xe ioctls */
>> +		struct rw_semaphore discovery_lock;
>>  	} eudebug;
>>  #endif
>>  
>> diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
>> index ea0cfd7697aa..c294e2c6152b 100644
>> --- a/drivers/gpu/drm/xe/xe_eudebug.c
>> +++ b/drivers/gpu/drm/xe/xe_eudebug.c
>> @@ -299,6 +299,8 @@ static bool xe_eudebug_detach(struct xe_device *xe,
>>  	}
>>  	spin_unlock(&d->connection.lock);
>>  
>> +	flush_work(&d->discovery_work);
>> +
>>  	if (!detached)
>>  		return false;
>>  
>> @@ -409,7 +411,7 @@ static struct task_struct *find_task_get(struct xe_file *xef)
>>  }
>>  
>>  static struct xe_eudebug *
>> -xe_eudebug_get(struct xe_file *xef)
>> +_xe_eudebug_get(struct xe_file *xef)
>>  {
>>  	struct task_struct *task;
>>  	struct xe_eudebug *d;
>> @@ -433,6 +435,24 @@ xe_eudebug_get(struct xe_file *xef)
>>  	return d;
>>  }
>>  
>> +static struct xe_eudebug *
>> +xe_eudebug_get(struct xe_file *xef)
>> +{
>> +	struct xe_eudebug *d;
>> +
>> +	lockdep_assert_held(&xef->xe->eudebug.discovery_lock);
>> +
>> +	d = _xe_eudebug_get(xef);
>> +	if (d) {
>> +		if (!completion_done(&d->discovery)) {
>> +			xe_eudebug_put(d);
>> +			d = NULL;
>> +		}
>> +	}
>> +
>> +	return d;
>> +}
>> +
>>  static int xe_eudebug_queue_event(struct xe_eudebug *d,
>>  				  struct xe_eudebug_event *event)
>>  {
>> @@ -810,6 +830,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,
>> @@ -832,6 +856,8 @@ static const struct file_operations fops = {
>>  	.unlocked_ioctl	= xe_eudebug_ioctl,
>>  };
>>  
>> +static void discovery_work_fn(struct work_struct *work);
>> +
>>  static int
>>  xe_eudebug_connect(struct xe_device *xe,
>>  		   struct drm_xe_eudebug_connect *param)
>> @@ -866,9 +892,11 @@ xe_eudebug_connect(struct xe_device *xe,
>>  	spin_lock_init(&d->connection.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);
>>  
>>  	d->res = xe_eudebug_resources_alloc();
>>  	if (IS_ERR(d->res)) {
>> @@ -886,6 +914,9 @@ xe_eudebug_connect(struct xe_device *xe,
>>  		goto err_detach;
>>  	}
>>  
>> +	kref_get(&d->ref);
>> +	queue_work(xe->eudebug.ordered_wq, &d->discovery_work);
>> +
>>  	eu_dbg(d, "connected session %lld", d->session);
>>  
>>  	return fd;
>> @@ -918,13 +949,18 @@ void xe_eudebug_init(struct xe_device *xe)
>>  	spin_lock_init(&xe->eudebug.lock);
>>  	INIT_LIST_HEAD(&xe->eudebug.list);
>>  	INIT_LIST_HEAD(&xe->clients.list);
>> +	init_rwsem(&xe->eudebug.discovery_lock);
>>  
>> -	xe->eudebug.available = true;
>> +	xe->eudebug.ordered_wq = alloc_ordered_workqueue("xe-eudebug-ordered-wq", 0);
>> +	xe->eudebug.available = !!xe->eudebug.ordered_wq;
>>  }
>>  
>>  void xe_eudebug_fini(struct xe_device *xe)
>>  {
>>  	xe_assert(xe, list_empty_careful(&xe->eudebug.list));
>> +
>> +	if (xe->eudebug.ordered_wq)
>> +		destroy_workqueue(xe->eudebug.ordered_wq);
>>  }
>>  
>>  static int send_open_event(struct xe_eudebug *d, u32 flags, const u64 handle,
>> @@ -990,21 +1026,25 @@ void xe_eudebug_file_open(struct xe_file *xef)
>>  	struct xe_eudebug *d;
>>  
>>  	INIT_LIST_HEAD(&xef->eudebug.client_link);
>> +
>> +	down_read(&xef->xe->eudebug.discovery_lock);
>> +
>>  	spin_lock(&xef->xe->clients.lock);
>>  	list_add_tail(&xef->eudebug.client_link, &xef->xe->clients.list);
>>  	spin_unlock(&xef->xe->clients.lock);
>>  
>>  	d = xe_eudebug_get(xef);
>
> This looks like this could deadlock.
>
> - discovery_work_fn is queued with &d->discovery not complete
> - This function runs and grabs &xef->xe->eudebug.discovery_lock in read
>   mode
> - completion_done(&d->discovery) is waited on xe_eudebug_get

If the completion is not done we just return NULL immediately.

Yes, previously when we actually used wait_for_completion() here
it was a problem. But in this version completion_done() does
not wait. Looking at completion_done(), the not done path is just
!READ_ONCE(x->done). Did you mean the taking the spinlock to
sync against the complete()?

-Mika

> - discovery_work_fn can't complete d->discovery) on
>   &xef->xe->eudebug.discovery_lock in write mode
>
> The summary is - it not safe to wait on '&d->discovery' while holding
> &xef->xe->eudebug.discovery_lock.
>
> Matt
>
>> -	if (!d)
>> -		return;
>> +	if (d)
>> +		xe_eudebug_event_put(d, client_create_event(d, xef));
>>  
>> -	xe_eudebug_event_put(d, client_create_event(d, xef));
>> +	up_read(&xef->xe->eudebug.discovery_lock);
>>  }
>>  
>>  void xe_eudebug_file_close(struct xe_file *xef)
>>  {
>>  	struct xe_eudebug *d;
>>  
>> +	down_read(&xef->xe->eudebug.discovery_lock);
>>  	d = xe_eudebug_get(xef);
>>  	if (d)
>>  		xe_eudebug_event_put(d, client_destroy_event(d, xef));
>> @@ -1012,6 +1052,8 @@ void xe_eudebug_file_close(struct xe_file *xef)
>>  	spin_lock(&xef->xe->clients.lock);
>>  	list_del_init(&xef->eudebug.client_link);
>>  	spin_unlock(&xef->xe->clients.lock);
>> +
>> +	up_read(&xef->xe->eudebug.discovery_lock);
>>  }
>>  
>>  static int send_vm_event(struct xe_eudebug *d, u32 flags,
>> @@ -1112,3 +1154,86 @@ 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 int discover_client(struct xe_eudebug *d, struct xe_file *xef)
>> +{
>> +	struct xe_vm *vm;
>> +	unsigned long i;
>> +	int err;
>> +
>> +	err = client_create_event(d, xef);
>> +	if (err)
>> +		return err;
>> +
>> +	xa_for_each(&xef->vm.xa, i, vm) {
>> +		err = vm_create_event(d, xef, vm);
>> +		if (err)
>> +			break;
>> +	}
>> +
>> +	return err;
>> +}
>> +
>> +static bool xe_eudebug_task_match(struct xe_eudebug *d, struct xe_file *xef)
>> +{
>> +	struct task_struct *task;
>> +	bool match;
>> +
>> +	task = find_task_get(xef);
>> +	if (!task)
>> +		return false;
>> +
>> +	match = same_thread_group(d->target_task, task);
>> +
>> +	put_task_struct(task);
>> +
>> +	return match;
>> +}
>> +
>> +static void discover_clients(struct xe_device *xe, struct xe_eudebug *d)
>> +{
>> +	struct xe_file *xef;
>> +	int err;
>> +
>> +	list_for_each_entry(xef, &xe->clients.list, eudebug.client_link) {
>> +		if (xe_eudebug_detached(d))
>> +			break;
>> +
>> +		if (xe_eudebug_task_match(d, xef))
>> +			err = discover_client(d, xef);
>> +		else
>> +			err = 0;
>> +
>> +		if (err) {
>> +			eu_dbg(d, "discover client %p: %d\n", xef, err);
>> +			break;
>> +		}
>> +	}
>> +}
>> +
>> +static void discovery_work_fn(struct work_struct *work)
>> +{
>> +	struct xe_eudebug *d = container_of(work, typeof(*d),
>> +					    discovery_work);
>> +	struct xe_device *xe = d->xe;
>> +
>> +	if (xe_eudebug_detached(d)) {
>> +		complete_all(&d->discovery);
>> +		xe_eudebug_put(d);
>> +		return;
>> +	}
>> +
>> +	down_write(&xe->eudebug.discovery_lock);
>> +
>> +	eu_dbg(d, "Discovery start for %lld\n", d->session);
>> +
>> +	discover_clients(xe, d);
>> +
>> +	eu_dbg(d, "Discovery end for %lld\n", d->session);
>> +
>> +	complete_all(&d->discovery);
>> +
>> +	up_write(&xe->eudebug.discovery_lock);
>> +
>> +	xe_eudebug_put(d);
>> +}
>> diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
>> index a5185f18f640..080a821db3e4 100644
>> --- a/drivers/gpu/drm/xe/xe_eudebug_types.h
>> +++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
>> @@ -19,6 +19,7 @@
>>  struct xe_device;
>>  struct task_struct;
>>  struct xe_eudebug_event;
>> +struct workqueue_struct;
>>  
>>  #define CONFIG_DRM_XE_DEBUGGER_EVENT_QUEUE_SIZE 64
>>  
>> @@ -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.34.1
>> 

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

* Re: [PATCH 03/18] drm/xe/eudebug: Introduce discovery for resources
  2024-10-03  7:27     ` Mika Kuoppala
@ 2024-10-03 16:32       ` Matthew Brost
  0 siblings, 0 replies; 40+ messages in thread
From: Matthew Brost @ 2024-10-03 16:32 UTC (permalink / raw)
  To: Mika Kuoppala; +Cc: intel-xe, Dominik Grzegorzek, Maciej Patelczyk

On Thu, Oct 03, 2024 at 10:27:52AM +0300, Mika Kuoppala wrote:
> Matthew Brost <matthew.brost@intel.com> writes:
> 
> > On Tue, Oct 01, 2024 at 05:42:51PM +0300, Mika Kuoppala wrote:
> >> Debugger connection can happen way after the client has
> >> created and destroyed arbitrary number of resources.
> >> 
> >> We need to playback all currently existing resources for the
> >> debugger. The client is held until this so called discovery
> >> process, executed by workqueue, is complete.
> >> 
> >> This patch is based on discovery work by Maciej Patelczyk
> >> for i915 driver.
> >> 
> >> v2: - use rw_semaphore to block drm_ioctls during discovery (Matthew)
> >>     - only lock according to ioctl at play (Dominik)
> >> 
> >> 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>
> >> ---
> >>  drivers/gpu/drm/xe/xe_device.c        |  10 +-
> >>  drivers/gpu/drm/xe/xe_device.h        |  34 +++++++
> >>  drivers/gpu/drm/xe/xe_device_types.h  |   6 ++
> >>  drivers/gpu/drm/xe/xe_eudebug.c       | 135 +++++++++++++++++++++++++-
> >>  drivers/gpu/drm/xe/xe_eudebug_types.h |   7 ++
> >>  5 files changed, 185 insertions(+), 7 deletions(-)
> >> 
> >> diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
> >> index 5615e2c23bf6..ec5eedbbf320 100644
> >> --- a/drivers/gpu/drm/xe/xe_device.c
> >> +++ b/drivers/gpu/drm/xe/xe_device.c
> >> @@ -215,8 +215,11 @@ static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> >>  		return -ECANCELED;
> >>  
> >>  	ret = xe_pm_runtime_get_ioctl(xe);
> >> -	if (ret >= 0)
> >> +	if (ret >= 0) {
> >> +		xe_eudebug_discovery_lock(xe, cmd);
> >>  		ret = drm_ioctl(file, cmd, arg);
> >> +		xe_eudebug_discovery_unlock(xe, cmd);
> >> +	}
> >>  	xe_pm_runtime_put(xe);
> >>  
> >>  	return ret;
> >> @@ -233,8 +236,11 @@ static long xe_drm_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo
> >>  		return -ECANCELED;
> >>  
> >>  	ret = xe_pm_runtime_get_ioctl(xe);
> >> -	if (ret >= 0)
> >> +	if (ret >= 0) {
> >> +		xe_eudebug_discovery_lock(xe, cmd);
> >>  		ret = drm_compat_ioctl(file, cmd, arg);
> >> +		xe_eudebug_discovery_unlock(xe, cmd);
> >> +	}
> >>  	xe_pm_runtime_put(xe);
> >>  
> >>  	return ret;
> >> diff --git a/drivers/gpu/drm/xe/xe_device.h b/drivers/gpu/drm/xe/xe_device.h
> >> index 4c3f0ebe78a9..b2fc85b1d26e 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"
> >> @@ -191,4 +192,37 @@ void xe_device_declare_wedged(struct xe_device *xe);
> >>  struct xe_file *xe_file_get(struct xe_file *xef);
> >>  void xe_file_put(struct xe_file *xef);
> >>  
> >> +#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
> >> +static inline int xe_eudebug_needs_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:
> >> +	case DRM_XE_EUDEBUG_CONNECT:
> >> +		return 1;
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static inline void xe_eudebug_discovery_lock(struct xe_device *xe, unsigned int cmd)
> >> +{
> >> +	if (xe_eudebug_needs_lock(cmd))
> >> +		down_read(&xe->eudebug.discovery_lock);
> >> +}
> >> +static inline void xe_eudebug_discovery_unlock(struct xe_device *xe, unsigned int cmd)
> >> +{
> >> +	if (xe_eudebug_needs_lock(cmd))
> >> +		up_read(&xe->eudebug.discovery_lock);
> >> +}
> >> +#else
> >> +static inline void xe_eudebug_discovery_lock(struct xe_device *xe, unsigned int cmd) { }
> >> +static inline void xe_eudebug_discovery_unlock(struct xe_device *xe, 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 cb4b52888a4b..54ceeee7cf75 100644
> >> --- a/drivers/gpu/drm/xe/xe_device_types.h
> >> +++ b/drivers/gpu/drm/xe/xe_device_types.h
> >> @@ -542,6 +542,12 @@ struct xe_device {
> >>  
> >>  		/** @available: is the debugging functionality available */
> >>  		bool available;
> >> +
> >> +		/** @ordered_wq: used to discovery */
> >> +		struct workqueue_struct *ordered_wq;
> >> +
> >> +		/** discovery_lock: used for discovery to block xe ioctls */
> >> +		struct rw_semaphore discovery_lock;
> >>  	} eudebug;
> >>  #endif
> >>  
> >> diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
> >> index ea0cfd7697aa..c294e2c6152b 100644
> >> --- a/drivers/gpu/drm/xe/xe_eudebug.c
> >> +++ b/drivers/gpu/drm/xe/xe_eudebug.c
> >> @@ -299,6 +299,8 @@ static bool xe_eudebug_detach(struct xe_device *xe,
> >>  	}
> >>  	spin_unlock(&d->connection.lock);
> >>  
> >> +	flush_work(&d->discovery_work);
> >> +
> >>  	if (!detached)
> >>  		return false;
> >>  
> >> @@ -409,7 +411,7 @@ static struct task_struct *find_task_get(struct xe_file *xef)
> >>  }
> >>  
> >>  static struct xe_eudebug *
> >> -xe_eudebug_get(struct xe_file *xef)
> >> +_xe_eudebug_get(struct xe_file *xef)
> >>  {
> >>  	struct task_struct *task;
> >>  	struct xe_eudebug *d;
> >> @@ -433,6 +435,24 @@ xe_eudebug_get(struct xe_file *xef)
> >>  	return d;
> >>  }
> >>  
> >> +static struct xe_eudebug *
> >> +xe_eudebug_get(struct xe_file *xef)
> >> +{
> >> +	struct xe_eudebug *d;
> >> +
> >> +	lockdep_assert_held(&xef->xe->eudebug.discovery_lock);
> >> +
> >> +	d = _xe_eudebug_get(xef);
> >> +	if (d) {
> >> +		if (!completion_done(&d->discovery)) {
> >> +			xe_eudebug_put(d);
> >> +			d = NULL;
> >> +		}
> >> +	}
> >> +
> >> +	return d;
> >> +}
> >> +
> >>  static int xe_eudebug_queue_event(struct xe_eudebug *d,
> >>  				  struct xe_eudebug_event *event)
> >>  {
> >> @@ -810,6 +830,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,
> >> @@ -832,6 +856,8 @@ static const struct file_operations fops = {
> >>  	.unlocked_ioctl	= xe_eudebug_ioctl,
> >>  };
> >>  
> >> +static void discovery_work_fn(struct work_struct *work);
> >> +
> >>  static int
> >>  xe_eudebug_connect(struct xe_device *xe,
> >>  		   struct drm_xe_eudebug_connect *param)
> >> @@ -866,9 +892,11 @@ xe_eudebug_connect(struct xe_device *xe,
> >>  	spin_lock_init(&d->connection.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);
> >>  
> >>  	d->res = xe_eudebug_resources_alloc();
> >>  	if (IS_ERR(d->res)) {
> >> @@ -886,6 +914,9 @@ xe_eudebug_connect(struct xe_device *xe,
> >>  		goto err_detach;
> >>  	}
> >>  
> >> +	kref_get(&d->ref);
> >> +	queue_work(xe->eudebug.ordered_wq, &d->discovery_work);
> >> +
> >>  	eu_dbg(d, "connected session %lld", d->session);
> >>  
> >>  	return fd;
> >> @@ -918,13 +949,18 @@ void xe_eudebug_init(struct xe_device *xe)
> >>  	spin_lock_init(&xe->eudebug.lock);
> >>  	INIT_LIST_HEAD(&xe->eudebug.list);
> >>  	INIT_LIST_HEAD(&xe->clients.list);
> >> +	init_rwsem(&xe->eudebug.discovery_lock);
> >>  
> >> -	xe->eudebug.available = true;
> >> +	xe->eudebug.ordered_wq = alloc_ordered_workqueue("xe-eudebug-ordered-wq", 0);
> >> +	xe->eudebug.available = !!xe->eudebug.ordered_wq;
> >>  }
> >>  
> >>  void xe_eudebug_fini(struct xe_device *xe)
> >>  {
> >>  	xe_assert(xe, list_empty_careful(&xe->eudebug.list));
> >> +
> >> +	if (xe->eudebug.ordered_wq)
> >> +		destroy_workqueue(xe->eudebug.ordered_wq);
> >>  }
> >>  
> >>  static int send_open_event(struct xe_eudebug *d, u32 flags, const u64 handle,
> >> @@ -990,21 +1026,25 @@ void xe_eudebug_file_open(struct xe_file *xef)
> >>  	struct xe_eudebug *d;
> >>  
> >>  	INIT_LIST_HEAD(&xef->eudebug.client_link);
> >> +
> >> +	down_read(&xef->xe->eudebug.discovery_lock);
> >> +
> >>  	spin_lock(&xef->xe->clients.lock);
> >>  	list_add_tail(&xef->eudebug.client_link, &xef->xe->clients.list);
> >>  	spin_unlock(&xef->xe->clients.lock);
> >>  
> >>  	d = xe_eudebug_get(xef);
> >
> > This looks like this could deadlock.
> >
> > - discovery_work_fn is queued with &d->discovery not complete
> > - This function runs and grabs &xef->xe->eudebug.discovery_lock in read
> >   mode
> > - completion_done(&d->discovery) is waited on xe_eudebug_get
> 
> If the completion is not done we just return NULL immediately.
> 

I misread the code, I was thinking xe_eudebug_get blocked on the
completion. This looks fine then - sorry the confusion noise.

The locking here looks much better than the previous revs.

From my review PoV - locking:
Acked-by: Matthew Brost <matthew.brost@intel.com>

> Yes, previously when we actually used wait_for_completion() here
> it was a problem. But in this version completion_done() does
> not wait. Looking at completion_done(), the not done path is just
> !READ_ONCE(x->done). Did you mean the taking the spinlock to
> sync against the complete()?
> 
> -Mika
> 
> > - discovery_work_fn can't complete d->discovery) on
> >   &xef->xe->eudebug.discovery_lock in write mode
> >
> > The summary is - it not safe to wait on '&d->discovery' while holding
> > &xef->xe->eudebug.discovery_lock.
> >
> > Matt
> >
> >> -	if (!d)
> >> -		return;
> >> +	if (d)
> >> +		xe_eudebug_event_put(d, client_create_event(d, xef));
> >>  
> >> -	xe_eudebug_event_put(d, client_create_event(d, xef));
> >> +	up_read(&xef->xe->eudebug.discovery_lock);
> >>  }
> >>  
> >>  void xe_eudebug_file_close(struct xe_file *xef)
> >>  {
> >>  	struct xe_eudebug *d;
> >>  
> >> +	down_read(&xef->xe->eudebug.discovery_lock);
> >>  	d = xe_eudebug_get(xef);
> >>  	if (d)
> >>  		xe_eudebug_event_put(d, client_destroy_event(d, xef));
> >> @@ -1012,6 +1052,8 @@ void xe_eudebug_file_close(struct xe_file *xef)
> >>  	spin_lock(&xef->xe->clients.lock);
> >>  	list_del_init(&xef->eudebug.client_link);
> >>  	spin_unlock(&xef->xe->clients.lock);
> >> +
> >> +	up_read(&xef->xe->eudebug.discovery_lock);
> >>  }
> >>  
> >>  static int send_vm_event(struct xe_eudebug *d, u32 flags,
> >> @@ -1112,3 +1154,86 @@ 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 int discover_client(struct xe_eudebug *d, struct xe_file *xef)
> >> +{
> >> +	struct xe_vm *vm;
> >> +	unsigned long i;
> >> +	int err;
> >> +
> >> +	err = client_create_event(d, xef);
> >> +	if (err)
> >> +		return err;
> >> +
> >> +	xa_for_each(&xef->vm.xa, i, vm) {
> >> +		err = vm_create_event(d, xef, vm);
> >> +		if (err)
> >> +			break;
> >> +	}
> >> +
> >> +	return err;
> >> +}
> >> +
> >> +static bool xe_eudebug_task_match(struct xe_eudebug *d, struct xe_file *xef)
> >> +{
> >> +	struct task_struct *task;
> >> +	bool match;
> >> +
> >> +	task = find_task_get(xef);
> >> +	if (!task)
> >> +		return false;
> >> +
> >> +	match = same_thread_group(d->target_task, task);
> >> +
> >> +	put_task_struct(task);
> >> +
> >> +	return match;
> >> +}
> >> +
> >> +static void discover_clients(struct xe_device *xe, struct xe_eudebug *d)
> >> +{
> >> +	struct xe_file *xef;
> >> +	int err;
> >> +
> >> +	list_for_each_entry(xef, &xe->clients.list, eudebug.client_link) {
> >> +		if (xe_eudebug_detached(d))
> >> +			break;
> >> +
> >> +		if (xe_eudebug_task_match(d, xef))
> >> +			err = discover_client(d, xef);
> >> +		else
> >> +			err = 0;
> >> +
> >> +		if (err) {
> >> +			eu_dbg(d, "discover client %p: %d\n", xef, err);
> >> +			break;
> >> +		}
> >> +	}
> >> +}
> >> +
> >> +static void discovery_work_fn(struct work_struct *work)
> >> +{
> >> +	struct xe_eudebug *d = container_of(work, typeof(*d),
> >> +					    discovery_work);
> >> +	struct xe_device *xe = d->xe;
> >> +
> >> +	if (xe_eudebug_detached(d)) {
> >> +		complete_all(&d->discovery);
> >> +		xe_eudebug_put(d);
> >> +		return;
> >> +	}
> >> +
> >> +	down_write(&xe->eudebug.discovery_lock);
> >> +
> >> +	eu_dbg(d, "Discovery start for %lld\n", d->session);
> >> +
> >> +	discover_clients(xe, d);
> >> +
> >> +	eu_dbg(d, "Discovery end for %lld\n", d->session);
> >> +
> >> +	complete_all(&d->discovery);
> >> +
> >> +	up_write(&xe->eudebug.discovery_lock);
> >> +
> >> +	xe_eudebug_put(d);
> >> +}
> >> diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
> >> index a5185f18f640..080a821db3e4 100644
> >> --- a/drivers/gpu/drm/xe/xe_eudebug_types.h
> >> +++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
> >> @@ -19,6 +19,7 @@
> >>  struct xe_device;
> >>  struct task_struct;
> >>  struct xe_eudebug_event;
> >> +struct workqueue_struct;
> >>  
> >>  #define CONFIG_DRM_XE_DEBUGGER_EVENT_QUEUE_SIZE 64
> >>  
> >> @@ -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.34.1
> >> 

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

* Re: [PATCH 12/18] drm/xe/eudebug: implement userptr_vma access
  2024-10-01 14:43 ` [PATCH 12/18] drm/xe/eudebug: implement userptr_vma access Mika Kuoppala
@ 2024-10-05  3:48   ` Matthew Brost
  2024-10-12  2:39   ` Matthew Brost
  2024-10-20 18:16   ` Matthew Brost
  2 siblings, 0 replies; 40+ messages in thread
From: Matthew Brost @ 2024-10-05  3:48 UTC (permalink / raw)
  To: Mika Kuoppala; +Cc: intel-xe, Andrzej Hajda, Maciej Patelczyk, Jonathan Cavitt

On Tue, Oct 01, 2024 at 05:43:00PM +0300, Mika Kuoppala wrote:
> From: Andrzej Hajda <andrzej.hajda@intel.com>
> 
> Debugger needs to read/write program's vmas including userptr_vma.
> Since hmm_range_fault is used to pin userptr vmas, it is possible
> to map those vmas from debugger context.
> 
> v2: pin pages vs notifier, move to vm.c (Matthew)
> 
> Signed-off-by: Andrzej Hajda <andrzej.hajda@intel.com>
> Signed-off-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
> Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
> Reviewed-by: Jonathan Cavitt <jonathan.cavitt@intel.com>
> ---
>  drivers/gpu/drm/xe/xe_eudebug.c |  2 +-
>  drivers/gpu/drm/xe/xe_vm.c      | 47 +++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/xe/xe_vm.h      |  3 +++
>  3 files changed, 51 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
> index edad6d533d0b..b09d7414cfe3 100644
> --- a/drivers/gpu/drm/xe/xe_eudebug.c
> +++ b/drivers/gpu/drm/xe/xe_eudebug.c
> @@ -3023,7 +3023,7 @@ static int xe_eudebug_vma_access(struct xe_vma *vma, u64 offset,
>  		return ret;
>  	}
>  
> -	return -EINVAL;
> +	return xe_uvma_access(to_userptr_vma(vma), offset, buf, bytes, write);
>  }
>  
>  static int xe_eudebug_vm_access(struct xe_vm *vm, u64 offset,
> diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
> index a836dfc5a86f..5f891e76993b 100644
> --- a/drivers/gpu/drm/xe/xe_vm.c
> +++ b/drivers/gpu/drm/xe/xe_vm.c
> @@ -3421,3 +3421,50 @@ void xe_vm_snapshot_free(struct xe_vm_snapshot *snap)
>  	}
>  	kvfree(snap);
>  }
> +
> +int xe_uvma_access(struct xe_userptr_vma *uvma, u64 offset,
> +		   void *buf, u64 len, bool write)

Nit:

s/xe_uvma_access/xe_vm_uvma_access/

Matt

> +{
> +	struct xe_vm *vm = xe_vma_vm(&uvma->vma);
> +	struct xe_userptr *up = &uvma->userptr;
> +	struct xe_res_cursor cur = {};
> +	int cur_len, ret = 0;
> +
> +	while (true) {
> +		down_read(&vm->userptr.notifier_lock);
> +		if (!xe_vma_userptr_check_repin(uvma))
> +			break;
> +
> +		spin_lock(&vm->userptr.invalidated_lock);
> +		list_del_init(&uvma->userptr.invalidate_link);
> +		spin_unlock(&vm->userptr.invalidated_lock);
> +
> +		up_read(&vm->userptr.notifier_lock);
> +		ret = xe_vma_userptr_pin_pages(uvma);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (!up->sg) {
> +		ret = -EINVAL;
> +		goto out_unlock_notifier;
> +	}
> +
> +	for (xe_res_first_sg(up->sg, offset, len, &cur); cur.remaining;
> +	     xe_res_next(&cur, cur_len)) {
> +		void *ptr = kmap_local_page(sg_page(cur.sgl)) + cur.start;
> +
> +		cur_len = min(cur.size, cur.remaining);
> +		if (write)
> +			memcpy(ptr, buf, cur_len);
> +		else
> +			memcpy(buf, ptr, cur_len);
> +		kunmap_local(ptr);
> +		buf += cur_len;
> +	}
> +	ret = len;
> +
> +out_unlock_notifier:
> +	up_read(&vm->userptr.notifier_lock);
> +	return ret;
> +}
> diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h
> index c864dba35e1d..99b9a9b011de 100644
> --- a/drivers/gpu/drm/xe/xe_vm.h
> +++ b/drivers/gpu/drm/xe/xe_vm.h
> @@ -281,3 +281,6 @@ struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm *vm);
>  void xe_vm_snapshot_capture_delayed(struct xe_vm_snapshot *snap);
>  void xe_vm_snapshot_print(struct xe_vm_snapshot *snap, struct drm_printer *p);
>  void xe_vm_snapshot_free(struct xe_vm_snapshot *snap);
> +
> +int xe_uvma_access(struct xe_userptr_vma *uvma, u64 offset,
> +		   void *buf, u64 len, bool write);
> -- 
> 2.34.1
> 

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

* Re: [PATCH 12/18] drm/xe/eudebug: implement userptr_vma access
  2024-10-01 14:43 ` [PATCH 12/18] drm/xe/eudebug: implement userptr_vma access Mika Kuoppala
  2024-10-05  3:48   ` Matthew Brost
@ 2024-10-12  2:39   ` Matthew Brost
  2024-10-12  2:55     ` Matthew Brost
  2024-10-20 18:16   ` Matthew Brost
  2 siblings, 1 reply; 40+ messages in thread
From: Matthew Brost @ 2024-10-12  2:39 UTC (permalink / raw)
  To: Mika Kuoppala; +Cc: intel-xe, Andrzej Hajda, Maciej Patelczyk, Jonathan Cavitt

On Tue, Oct 01, 2024 at 05:43:00PM +0300, Mika Kuoppala wrote:
> From: Andrzej Hajda <andrzej.hajda@intel.com>
> 
> Debugger needs to read/write program's vmas including userptr_vma.
> Since hmm_range_fault is used to pin userptr vmas, it is possible
> to map those vmas from debugger context.
> 
> v2: pin pages vs notifier, move to vm.c (Matthew)
> 
> Signed-off-by: Andrzej Hajda <andrzej.hajda@intel.com>
> Signed-off-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
> Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
> Reviewed-by: Jonathan Cavitt <jonathan.cavitt@intel.com>
> ---
>  drivers/gpu/drm/xe/xe_eudebug.c |  2 +-
>  drivers/gpu/drm/xe/xe_vm.c      | 47 +++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/xe/xe_vm.h      |  3 +++
>  3 files changed, 51 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
> index edad6d533d0b..b09d7414cfe3 100644
> --- a/drivers/gpu/drm/xe/xe_eudebug.c
> +++ b/drivers/gpu/drm/xe/xe_eudebug.c
> @@ -3023,7 +3023,7 @@ static int xe_eudebug_vma_access(struct xe_vma *vma, u64 offset,
>  		return ret;
>  	}
>  
> -	return -EINVAL;
> +	return xe_uvma_access(to_userptr_vma(vma), offset, buf, bytes, write);
>  }
>  
>  static int xe_eudebug_vm_access(struct xe_vm *vm, u64 offset,
> diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
> index a836dfc5a86f..5f891e76993b 100644
> --- a/drivers/gpu/drm/xe/xe_vm.c
> +++ b/drivers/gpu/drm/xe/xe_vm.c
> @@ -3421,3 +3421,50 @@ void xe_vm_snapshot_free(struct xe_vm_snapshot *snap)
>  	}
>  	kvfree(snap);
>  }
> +
> +int xe_uvma_access(struct xe_userptr_vma *uvma, u64 offset,
> +		   void *buf, u64 len, bool write)
> +{
> +	struct xe_vm *vm = xe_vma_vm(&uvma->vma);
> +	struct xe_userptr *up = &uvma->userptr;
> +	struct xe_res_cursor cur = {};
> +	int cur_len, ret = 0;
> +
> +	while (true) {
> +		down_read(&vm->userptr.notifier_lock);
> +		if (!xe_vma_userptr_check_repin(uvma))
> +			break;
> +
> +		spin_lock(&vm->userptr.invalidated_lock);
> +		list_del_init(&uvma->userptr.invalidate_link);
> +		spin_unlock(&vm->userptr.invalidated_lock);
> +
> +		up_read(&vm->userptr.notifier_lock);
> +		ret = xe_vma_userptr_pin_pages(uvma);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (!up->sg) {
> +		ret = -EINVAL;
> +		goto out_unlock_notifier;
> +	}
> +
> +	for (xe_res_first_sg(up->sg, offset, len, &cur); cur.remaining;
> +	     xe_res_next(&cur, cur_len)) {

This doesn't look right after reviewing [1].

A SG list is collection of IOVA which may be contain non-contiguous
physical pages.

I'm pretty sure if the EU debugger is enable you are going to have to
save off all the pages returned from hmm_range_fault and kmap each page
individually.

Matt

[1] https://patchwork.freedesktop.org/patch/619324/?series=139780&rev=3

> +		void *ptr = kmap_local_page(sg_page(cur.sgl)) + cur.start;
> +
> +		cur_len = min(cur.size, cur.remaining);
> +		if (write)
> +			memcpy(ptr, buf, cur_len);
> +		else
> +			memcpy(buf, ptr, cur_len);
> +		kunmap_local(ptr);
> +		buf += cur_len;
> +	}
> +	ret = len;
> +
> +out_unlock_notifier:
> +	up_read(&vm->userptr.notifier_lock);
> +	return ret;
> +}
> diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h
> index c864dba35e1d..99b9a9b011de 100644
> --- a/drivers/gpu/drm/xe/xe_vm.h
> +++ b/drivers/gpu/drm/xe/xe_vm.h
> @@ -281,3 +281,6 @@ struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm *vm);
>  void xe_vm_snapshot_capture_delayed(struct xe_vm_snapshot *snap);
>  void xe_vm_snapshot_print(struct xe_vm_snapshot *snap, struct drm_printer *p);
>  void xe_vm_snapshot_free(struct xe_vm_snapshot *snap);
> +
> +int xe_uvma_access(struct xe_userptr_vma *uvma, u64 offset,
> +		   void *buf, u64 len, bool write);
> -- 
> 2.34.1
> 

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

* Re: [PATCH 12/18] drm/xe/eudebug: implement userptr_vma access
  2024-10-12  2:39   ` Matthew Brost
@ 2024-10-12  2:55     ` Matthew Brost
  0 siblings, 0 replies; 40+ messages in thread
From: Matthew Brost @ 2024-10-12  2:55 UTC (permalink / raw)
  To: Mika Kuoppala; +Cc: intel-xe, Andrzej Hajda, Maciej Patelczyk, Jonathan Cavitt

On Sat, Oct 12, 2024 at 02:39:39AM +0000, Matthew Brost wrote:
> On Tue, Oct 01, 2024 at 05:43:00PM +0300, Mika Kuoppala wrote:
> > From: Andrzej Hajda <andrzej.hajda@intel.com>
> > 
> > Debugger needs to read/write program's vmas including userptr_vma.
> > Since hmm_range_fault is used to pin userptr vmas, it is possible
> > to map those vmas from debugger context.
> > 
> > v2: pin pages vs notifier, move to vm.c (Matthew)
> > 
> > Signed-off-by: Andrzej Hajda <andrzej.hajda@intel.com>
> > Signed-off-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
> > Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
> > Reviewed-by: Jonathan Cavitt <jonathan.cavitt@intel.com>
> > ---
> >  drivers/gpu/drm/xe/xe_eudebug.c |  2 +-
> >  drivers/gpu/drm/xe/xe_vm.c      | 47 +++++++++++++++++++++++++++++++++
> >  drivers/gpu/drm/xe/xe_vm.h      |  3 +++
> >  3 files changed, 51 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
> > index edad6d533d0b..b09d7414cfe3 100644
> > --- a/drivers/gpu/drm/xe/xe_eudebug.c
> > +++ b/drivers/gpu/drm/xe/xe_eudebug.c
> > @@ -3023,7 +3023,7 @@ static int xe_eudebug_vma_access(struct xe_vma *vma, u64 offset,
> >  		return ret;
> >  	}
> >  
> > -	return -EINVAL;
> > +	return xe_uvma_access(to_userptr_vma(vma), offset, buf, bytes, write);
> >  }
> >  
> >  static int xe_eudebug_vm_access(struct xe_vm *vm, u64 offset,
> > diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
> > index a836dfc5a86f..5f891e76993b 100644
> > --- a/drivers/gpu/drm/xe/xe_vm.c
> > +++ b/drivers/gpu/drm/xe/xe_vm.c
> > @@ -3421,3 +3421,50 @@ void xe_vm_snapshot_free(struct xe_vm_snapshot *snap)
> >  	}
> >  	kvfree(snap);
> >  }
> > +
> > +int xe_uvma_access(struct xe_userptr_vma *uvma, u64 offset,
> > +		   void *buf, u64 len, bool write)
> > +{
> > +	struct xe_vm *vm = xe_vma_vm(&uvma->vma);
> > +	struct xe_userptr *up = &uvma->userptr;
> > +	struct xe_res_cursor cur = {};
> > +	int cur_len, ret = 0;
> > +
> > +	while (true) {
> > +		down_read(&vm->userptr.notifier_lock);
> > +		if (!xe_vma_userptr_check_repin(uvma))
> > +			break;
> > +
> > +		spin_lock(&vm->userptr.invalidated_lock);
> > +		list_del_init(&uvma->userptr.invalidate_link);
> > +		spin_unlock(&vm->userptr.invalidated_lock);
> > +
> > +		up_read(&vm->userptr.notifier_lock);
> > +		ret = xe_vma_userptr_pin_pages(uvma);
> > +		if (ret)
> > +			return ret;
> > +	}
> > +
> > +	if (!up->sg) {
> > +		ret = -EINVAL;
> > +		goto out_unlock_notifier;
> > +	}
> > +
> > +	for (xe_res_first_sg(up->sg, offset, len, &cur); cur.remaining;
> > +	     xe_res_next(&cur, cur_len)) {
> 
> This doesn't look right after reviewing [1].
> 
> A SG list is collection of IOVA which may be contain non-contiguous
> physical pages.
> 

This is unclear, let me try again.

A SG list is a collection of IOVA aka dma address. This is the the view
from the device to CPU's memory. Each IOVA may be a non-contiguous set
of physical pages if the IOMMU is on. Thus you can't just look at the
first page of the IOVA and know what the 2nd page is.

Hope this makes a bit more sense.

Matt
 
> I'm pretty sure if the EU debugger is enable you are going to have to
> save off all the pages returned from hmm_range_fault and kmap each page
> individually.
> 
> Matt
> 
> [1] https://patchwork.freedesktop.org/patch/619324/?series=139780&rev=3
> 
> > +		void *ptr = kmap_local_page(sg_page(cur.sgl)) + cur.start;
> > +
> > +		cur_len = min(cur.size, cur.remaining);
> > +		if (write)
> > +			memcpy(ptr, buf, cur_len);
> > +		else
> > +			memcpy(buf, ptr, cur_len);
> > +		kunmap_local(ptr);
> > +		buf += cur_len;
> > +	}
> > +	ret = len;
> > +
> > +out_unlock_notifier:
> > +	up_read(&vm->userptr.notifier_lock);
> > +	return ret;
> > +}
> > diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h
> > index c864dba35e1d..99b9a9b011de 100644
> > --- a/drivers/gpu/drm/xe/xe_vm.h
> > +++ b/drivers/gpu/drm/xe/xe_vm.h
> > @@ -281,3 +281,6 @@ struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm *vm);
> >  void xe_vm_snapshot_capture_delayed(struct xe_vm_snapshot *snap);
> >  void xe_vm_snapshot_print(struct xe_vm_snapshot *snap, struct drm_printer *p);
> >  void xe_vm_snapshot_free(struct xe_vm_snapshot *snap);
> > +
> > +int xe_uvma_access(struct xe_userptr_vma *uvma, u64 offset,
> > +		   void *buf, u64 len, bool write);
> > -- 
> > 2.34.1
> > 

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

* Re: [PATCH 12/18] drm/xe/eudebug: implement userptr_vma access
  2024-10-01 14:43 ` [PATCH 12/18] drm/xe/eudebug: implement userptr_vma access Mika Kuoppala
  2024-10-05  3:48   ` Matthew Brost
  2024-10-12  2:39   ` Matthew Brost
@ 2024-10-20 18:16   ` Matthew Brost
  2024-10-21  9:54     ` Hajda, Andrzej
  2 siblings, 1 reply; 40+ messages in thread
From: Matthew Brost @ 2024-10-20 18:16 UTC (permalink / raw)
  To: Mika Kuoppala; +Cc: intel-xe, Andrzej Hajda, Maciej Patelczyk, Jonathan Cavitt

On Tue, Oct 01, 2024 at 05:43:00PM +0300, Mika Kuoppala wrote:
> From: Andrzej Hajda <andrzej.hajda@intel.com>
> 
> Debugger needs to read/write program's vmas including userptr_vma.
> Since hmm_range_fault is used to pin userptr vmas, it is possible
> to map those vmas from debugger context.
> 
> v2: pin pages vs notifier, move to vm.c (Matthew)
> 
> Signed-off-by: Andrzej Hajda <andrzej.hajda@intel.com>
> Signed-off-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
> Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
> Reviewed-by: Jonathan Cavitt <jonathan.cavitt@intel.com>
> ---
>  drivers/gpu/drm/xe/xe_eudebug.c |  2 +-
>  drivers/gpu/drm/xe/xe_vm.c      | 47 +++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/xe/xe_vm.h      |  3 +++
>  3 files changed, 51 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
> index edad6d533d0b..b09d7414cfe3 100644
> --- a/drivers/gpu/drm/xe/xe_eudebug.c
> +++ b/drivers/gpu/drm/xe/xe_eudebug.c
> @@ -3023,7 +3023,7 @@ static int xe_eudebug_vma_access(struct xe_vma *vma, u64 offset,
>  		return ret;
>  	}
>  
> -	return -EINVAL;
> +	return xe_uvma_access(to_userptr_vma(vma), offset, buf, bytes, write);
>  }
>  
>  static int xe_eudebug_vm_access(struct xe_vm *vm, u64 offset,
> diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
> index a836dfc5a86f..5f891e76993b 100644
> --- a/drivers/gpu/drm/xe/xe_vm.c
> +++ b/drivers/gpu/drm/xe/xe_vm.c
> @@ -3421,3 +3421,50 @@ void xe_vm_snapshot_free(struct xe_vm_snapshot *snap)
>  	}
>  	kvfree(snap);
>  }
> +
> +int xe_uvma_access(struct xe_userptr_vma *uvma, u64 offset,
> +		   void *buf, u64 len, bool write)
> +{

Maybe dump question but are we overthinking this here?

Can we just use kthread_use_mm, copy_to_user, copy_from_user?

If not then my previous comments still apply here.

Matt

> +	struct xe_vm *vm = xe_vma_vm(&uvma->vma);
> +	struct xe_userptr *up = &uvma->userptr;
> +	struct xe_res_cursor cur = {};
> +	int cur_len, ret = 0;
> +
> +	while (true) {
> +		down_read(&vm->userptr.notifier_lock);
> +		if (!xe_vma_userptr_check_repin(uvma))
> +			break;
> +
> +		spin_lock(&vm->userptr.invalidated_lock);
> +		list_del_init(&uvma->userptr.invalidate_link);
> +		spin_unlock(&vm->userptr.invalidated_lock);
> +
> +		up_read(&vm->userptr.notifier_lock);
> +		ret = xe_vma_userptr_pin_pages(uvma);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (!up->sg) {
> +		ret = -EINVAL;
> +		goto out_unlock_notifier;
> +	}
> +
> +	for (xe_res_first_sg(up->sg, offset, len, &cur); cur.remaining;
> +	     xe_res_next(&cur, cur_len)) {
> +		void *ptr = kmap_local_page(sg_page(cur.sgl)) + cur.start;
> +
> +		cur_len = min(cur.size, cur.remaining);
> +		if (write)
> +			memcpy(ptr, buf, cur_len);
> +		else
> +			memcpy(buf, ptr, cur_len);
> +		kunmap_local(ptr);
> +		buf += cur_len;
> +	}
> +	ret = len;
> +
> +out_unlock_notifier:
> +	up_read(&vm->userptr.notifier_lock);
> +	return ret;
> +}
> diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h
> index c864dba35e1d..99b9a9b011de 100644
> --- a/drivers/gpu/drm/xe/xe_vm.h
> +++ b/drivers/gpu/drm/xe/xe_vm.h
> @@ -281,3 +281,6 @@ struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm *vm);
>  void xe_vm_snapshot_capture_delayed(struct xe_vm_snapshot *snap);
>  void xe_vm_snapshot_print(struct xe_vm_snapshot *snap, struct drm_printer *p);
>  void xe_vm_snapshot_free(struct xe_vm_snapshot *snap);
> +
> +int xe_uvma_access(struct xe_userptr_vma *uvma, u64 offset,
> +		   void *buf, u64 len, bool write);
> -- 
> 2.34.1
> 

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

* Re: [PATCH 12/18] drm/xe/eudebug: implement userptr_vma access
  2024-10-20 18:16   ` Matthew Brost
@ 2024-10-21  9:54     ` Hajda, Andrzej
  2024-10-21 22:34       ` Matthew Brost
  0 siblings, 1 reply; 40+ messages in thread
From: Hajda, Andrzej @ 2024-10-21  9:54 UTC (permalink / raw)
  To: Matthew Brost, Mika Kuoppala; +Cc: intel-xe, Maciej Patelczyk, Jonathan Cavitt

W dniu 20.10.2024 o 20:16, Matthew Brost pisze:
> On Tue, Oct 01, 2024 at 05:43:00PM +0300, Mika Kuoppala wrote:
>> From: Andrzej Hajda <andrzej.hajda@intel.com>
>>
>> Debugger needs to read/write program's vmas including userptr_vma.
>> Since hmm_range_fault is used to pin userptr vmas, it is possible
>> to map those vmas from debugger context.
>>
>> v2: pin pages vs notifier, move to vm.c (Matthew)
>>
>> Signed-off-by: Andrzej Hajda <andrzej.hajda@intel.com>
>> Signed-off-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
>> Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
>> Reviewed-by: Jonathan Cavitt <jonathan.cavitt@intel.com>
>> ---
>>   drivers/gpu/drm/xe/xe_eudebug.c |  2 +-
>>   drivers/gpu/drm/xe/xe_vm.c      | 47 +++++++++++++++++++++++++++++++++
>>   drivers/gpu/drm/xe/xe_vm.h      |  3 +++
>>   3 files changed, 51 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
>> index edad6d533d0b..b09d7414cfe3 100644
>> --- a/drivers/gpu/drm/xe/xe_eudebug.c
>> +++ b/drivers/gpu/drm/xe/xe_eudebug.c
>> @@ -3023,7 +3023,7 @@ static int xe_eudebug_vma_access(struct xe_vma *vma, u64 offset,
>>   		return ret;
>>   	}
>>   
>> -	return -EINVAL;
>> +	return xe_uvma_access(to_userptr_vma(vma), offset, buf, bytes, write);
>>   }
>>   
>>   static int xe_eudebug_vm_access(struct xe_vm *vm, u64 offset,
>> diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
>> index a836dfc5a86f..5f891e76993b 100644
>> --- a/drivers/gpu/drm/xe/xe_vm.c
>> +++ b/drivers/gpu/drm/xe/xe_vm.c
>> @@ -3421,3 +3421,50 @@ void xe_vm_snapshot_free(struct xe_vm_snapshot *snap)
>>   	}
>>   	kvfree(snap);
>>   }
>> +
>> +int xe_uvma_access(struct xe_userptr_vma *uvma, u64 offset,
>> +		   void *buf, u64 len, bool write)
>> +{
> 
> Maybe dump question but are we overthinking this here?
> 
> Can we just use kthread_use_mm, copy_to_user, copy_from_user?
> 
> If not then my previous comments still apply here.

This function is called from debugger process context and kthread_use_mm 
is allowed only from kthread. Spawning kthread just for this is an 
option but looks odd and suboptimal, could be kind of last resort, or not?

Another options:
1. Keep reference to remote task in xe_userptr and use 
access_process_vm(up->task, ...).

2. Pass xe_eudebug.target_task reference down from eudebug framework to 
this helper and use access_process_vm. Current call chain is:
__xe_eudebug_vm_access - has access to xe_eudebug.target_task
->__vm_read_write
--->xe_eudebug_vm_access
---->xe_eudebug_vm_access
----->xe_eudebug_vma_access
------>xe_vm_userptr_access
So to achieve this multiple changes are required, but maybe it is valid 
path to go?
One potential issue with 1 and 2 is that multiple UMD tests were failing 
when access_process_vm/access_remote_vm were used, they were not 
investigated as this approach was dropped due to different reasons.

3. Continue approach from this patch, but with corrected page iterator 
of up->sg sg list[1]. This was nacked by you(?) [2] but I have problem 
understanding why? I see lot of code in kernel mapping sg pages:
linux$ git grep ' kmap.*sg' | wc -l
61
Is it incorrect? Or our case is different?

4. As you suggested in [3](?), modify xe_hmm_userptr_populate_range to 
keep hmm_range.hmm_pfns(or sth similar) in xe_userptr and use it later 
(instead of up->sg) to iterate over pages.

[1]: 
https://lore.kernel.org/intel-xe/20241011-xe_res_cursor_add_page_iterator-v3-1-0f8b8d3ab021@intel.com/
[2]: 
https://lore.kernel.org/intel-xe/Zw32fauoUmB6Iojk@DUT025-TGLU.fm.intel.com/
[3]: 
https://patchwork.freedesktop.org/patch/617481/?series=136572&rev=2#comment_1126527

Regards
Andrzej

> 
> Matt
> 
>> +	struct xe_vm *vm = xe_vma_vm(&uvma->vma);
>> +	struct xe_userptr *up = &uvma->userptr;
>> +	struct xe_res_cursor cur = {};
>> +	int cur_len, ret = 0;
>> +
>> +	while (true) {
>> +		down_read(&vm->userptr.notifier_lock);
>> +		if (!xe_vma_userptr_check_repin(uvma))
>> +			break;
>> +
>> +		spin_lock(&vm->userptr.invalidated_lock);
>> +		list_del_init(&uvma->userptr.invalidate_link);
>> +		spin_unlock(&vm->userptr.invalidated_lock);
>> +
>> +		up_read(&vm->userptr.notifier_lock);
>> +		ret = xe_vma_userptr_pin_pages(uvma);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +
>> +	if (!up->sg) {
>> +		ret = -EINVAL;
>> +		goto out_unlock_notifier;
>> +	}
>> +
>> +	for (xe_res_first_sg(up->sg, offset, len, &cur); cur.remaining;
>> +	     xe_res_next(&cur, cur_len)) {
>> +		void *ptr = kmap_local_page(sg_page(cur.sgl)) + cur.start;
>> +
>> +		cur_len = min(cur.size, cur.remaining);
>> +		if (write)
>> +			memcpy(ptr, buf, cur_len);
>> +		else
>> +			memcpy(buf, ptr, cur_len);
>> +		kunmap_local(ptr);
>> +		buf += cur_len;
>> +	}
>> +	ret = len;
>> +
>> +out_unlock_notifier:
>> +	up_read(&vm->userptr.notifier_lock);
>> +	return ret;
>> +}
>> diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h
>> index c864dba35e1d..99b9a9b011de 100644
>> --- a/drivers/gpu/drm/xe/xe_vm.h
>> +++ b/drivers/gpu/drm/xe/xe_vm.h
>> @@ -281,3 +281,6 @@ struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm *vm);
>>   void xe_vm_snapshot_capture_delayed(struct xe_vm_snapshot *snap);
>>   void xe_vm_snapshot_print(struct xe_vm_snapshot *snap, struct drm_printer *p);
>>   void xe_vm_snapshot_free(struct xe_vm_snapshot *snap);
>> +
>> +int xe_uvma_access(struct xe_userptr_vma *uvma, u64 offset,
>> +		   void *buf, u64 len, bool write);
>> -- 
>> 2.34.1
>>


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

* Re: [PATCH 12/18] drm/xe/eudebug: implement userptr_vma access
  2024-10-21  9:54     ` Hajda, Andrzej
@ 2024-10-21 22:34       ` Matthew Brost
  2024-10-23 11:32         ` Hajda, Andrzej
  0 siblings, 1 reply; 40+ messages in thread
From: Matthew Brost @ 2024-10-21 22:34 UTC (permalink / raw)
  To: Hajda, Andrzej; +Cc: Mika Kuoppala, intel-xe, Maciej Patelczyk, Jonathan Cavitt

On Mon, Oct 21, 2024 at 11:54:30AM +0200, Hajda, Andrzej wrote:
> W dniu 20.10.2024 o 20:16, Matthew Brost pisze:
> > On Tue, Oct 01, 2024 at 05:43:00PM +0300, Mika Kuoppala wrote:
> > > From: Andrzej Hajda <andrzej.hajda@intel.com>
> > > 
> > > Debugger needs to read/write program's vmas including userptr_vma.
> > > Since hmm_range_fault is used to pin userptr vmas, it is possible
> > > to map those vmas from debugger context.
> > > 
> > > v2: pin pages vs notifier, move to vm.c (Matthew)
> > > 
> > > Signed-off-by: Andrzej Hajda <andrzej.hajda@intel.com>
> > > Signed-off-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
> > > Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
> > > Reviewed-by: Jonathan Cavitt <jonathan.cavitt@intel.com>
> > > ---
> > >   drivers/gpu/drm/xe/xe_eudebug.c |  2 +-
> > >   drivers/gpu/drm/xe/xe_vm.c      | 47 +++++++++++++++++++++++++++++++++
> > >   drivers/gpu/drm/xe/xe_vm.h      |  3 +++
> > >   3 files changed, 51 insertions(+), 1 deletion(-)
> > > 
> > > diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
> > > index edad6d533d0b..b09d7414cfe3 100644
> > > --- a/drivers/gpu/drm/xe/xe_eudebug.c
> > > +++ b/drivers/gpu/drm/xe/xe_eudebug.c
> > > @@ -3023,7 +3023,7 @@ static int xe_eudebug_vma_access(struct xe_vma *vma, u64 offset,
> > >   		return ret;
> > >   	}
> > > -	return -EINVAL;
> > > +	return xe_uvma_access(to_userptr_vma(vma), offset, buf, bytes, write);
> > >   }
> > >   static int xe_eudebug_vm_access(struct xe_vm *vm, u64 offset,
> > > diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
> > > index a836dfc5a86f..5f891e76993b 100644
> > > --- a/drivers/gpu/drm/xe/xe_vm.c
> > > +++ b/drivers/gpu/drm/xe/xe_vm.c
> > > @@ -3421,3 +3421,50 @@ void xe_vm_snapshot_free(struct xe_vm_snapshot *snap)
> > >   	}
> > >   	kvfree(snap);
> > >   }
> > > +
> > > +int xe_uvma_access(struct xe_userptr_vma *uvma, u64 offset,
> > > +		   void *buf, u64 len, bool write)
> > > +{
> > 
> > Maybe dump question but are we overthinking this here?
> > 
> > Can we just use kthread_use_mm, copy_to_user, copy_from_user?
> > 
> > If not then my previous comments still apply here.
> 
> This function is called from debugger process context and kthread_use_mm is
> allowed only from kthread. Spawning kthread just for this is an option but
> looks odd and suboptimal, could be kind of last resort, or not?
> 
> Another options:
> 1. Keep reference to remote task in xe_userptr and use
> access_process_vm(up->task, ...).
> 

I think remote refs are generally a bad idea but admittedly don't fully
understand what this would look like.

> 2. Pass xe_eudebug.target_task reference down from eudebug framework to this
> helper and use access_process_vm. Current call chain is:
> __xe_eudebug_vm_access - has access to xe_eudebug.target_task
> ->__vm_read_write
> --->xe_eudebug_vm_access
> ---->xe_eudebug_vm_access
> ----->xe_eudebug_vma_access
> ------>xe_vm_userptr_access
> So to achieve this multiple changes are required, but maybe it is valid path
> to go?
> One potential issue with 1 and 2 is that multiple UMD tests were failing
> when access_process_vm/access_remote_vm were used, they were not
> investigated as this approach was dropped due to different reasons.
> 
> 3. Continue approach from this patch, but with corrected page iterator of
> up->sg sg list[1]. This was nacked by you(?) [2] but I have problem
> understanding why? I see lot of code in kernel mapping sg pages:
> linux$ git grep ' kmap.*sg' | wc -l

I looked here every example I found has mapping and accessing 1 page at
time not mapping 1 page and accessing many.

> 61
> Is it incorrect? Or our case is different?
> 

A sglist segments are dma-addresses (virtual address), thus every 4k in
the segment can be a different physical page.

i.e., look at this snippet:

+		void *ptr = kmap_local_page(sg_page(cur.sgl)) + cur.start;
+
+		cur_len = min(cur.size, cur.remaining);
+		if (write)
+			memcpy(ptr, buf, cur_len);
+		else
+			memcpy(buf, ptr, cur_len);
+		kunmap_local(ptr);

If 'cur.start' > 4k, then you are potentially pointing to an incorrect
page and corrupting memory.

Likewise if 'cur_len' > 4k, then you are potentially pointing to an
incorrect page and corrupting memory.

This loop would have to be changed to something like below which kmaps
and accesses 1 page at a time...
	
	for (xe_res_first_sg(up->sg, offset, len, &cur); cur.remaining;
	     xe_res_next(&cur, cur_len)) {
		int segment_len;
		int remain;		

		cur_len = min(cur.size, cur.remaining);
		remain = cur_len;

		for (i = 0; i < cur_len; i += segment_len) {
			phys_addr_t phys = iommu_iova_to_phys(sg_dma_address(cur.sgl) + i + cur.start);
			struct page *page = phys_to_page(phys); 
			void *ptr = kmap_local_page(page);
			int ptr_offset = offset & ~PAGE_MASK;

			segment_len = min(remain, PAGE_SIZE - ptr_offset);

			if (write)
				memcpy(ptr + ptr_offset, buf + i, segment_len);
			else
				memcpy(buf + i, ptr + ptr_offset, segment_len);
			kunmap_local(ptr);

			offset += segment_len;
			remain -= segment_len;		
		}
		
		buf += cur_len;
	}

> 4. As you suggested in [3](?), modify xe_hmm_userptr_populate_range to keep
> hmm_range.hmm_pfns(or sth similar) in xe_userptr and use it later (instead
> of up->sg) to iterate over pages.
>

Or just call hmm_range_fault directly here and operate on returned pages
directly.

BTW eventually all the userptr stuff is going to change and be based on
GPU SVM [4]. Calling hmm_range_fault directly will always work though
and likely the safest option.

Matt

[4] https://patchwork.freedesktop.org/patch/619809/?series=137870&rev=2
 
> [1]: https://lore.kernel.org/intel-xe/20241011-xe_res_cursor_add_page_iterator-v3-1-0f8b8d3ab021@intel.com/
> [2]:
> https://lore.kernel.org/intel-xe/Zw32fauoUmB6Iojk@DUT025-TGLU.fm.intel.com/
> [3]: https://patchwork.freedesktop.org/patch/617481/?series=136572&rev=2#comment_1126527
> 
> Regards
> Andrzej
> 
> > 
> > Matt
> > 
> > > +	struct xe_vm *vm = xe_vma_vm(&uvma->vma);
> > > +	struct xe_userptr *up = &uvma->userptr;
> > > +	struct xe_res_cursor cur = {};
> > > +	int cur_len, ret = 0;
> > > +
> > > +	while (true) {
> > > +		down_read(&vm->userptr.notifier_lock);
> > > +		if (!xe_vma_userptr_check_repin(uvma))
> > > +			break;
> > > +
> > > +		spin_lock(&vm->userptr.invalidated_lock);
> > > +		list_del_init(&uvma->userptr.invalidate_link);
> > > +		spin_unlock(&vm->userptr.invalidated_lock);
> > > +
> > > +		up_read(&vm->userptr.notifier_lock);
> > > +		ret = xe_vma_userptr_pin_pages(uvma);
> > > +		if (ret)
> > > +			return ret;
> > > +	}
> > > +
> > > +	if (!up->sg) {
> > > +		ret = -EINVAL;
> > > +		goto out_unlock_notifier;
> > > +	}
> > > +
> > > +	for (xe_res_first_sg(up->sg, offset, len, &cur); cur.remaining;
> > > +	     xe_res_next(&cur, cur_len)) {
> > > +		void *ptr = kmap_local_page(sg_page(cur.sgl)) + cur.start;
> > > +
> > > +		cur_len = min(cur.size, cur.remaining);
> > > +		if (write)
> > > +			memcpy(ptr, buf, cur_len);
> > > +		else
> > > +			memcpy(buf, ptr, cur_len);
> > > +		kunmap_local(ptr);
> > > +		buf += cur_len;
> > > +	}
> > > +	ret = len;
> > > +
> > > +out_unlock_notifier:
> > > +	up_read(&vm->userptr.notifier_lock);
> > > +	return ret;
> > > +}
> > > diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h
> > > index c864dba35e1d..99b9a9b011de 100644
> > > --- a/drivers/gpu/drm/xe/xe_vm.h
> > > +++ b/drivers/gpu/drm/xe/xe_vm.h
> > > @@ -281,3 +281,6 @@ struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm *vm);
> > >   void xe_vm_snapshot_capture_delayed(struct xe_vm_snapshot *snap);
> > >   void xe_vm_snapshot_print(struct xe_vm_snapshot *snap, struct drm_printer *p);
> > >   void xe_vm_snapshot_free(struct xe_vm_snapshot *snap);
> > > +
> > > +int xe_uvma_access(struct xe_userptr_vma *uvma, u64 offset,
> > > +		   void *buf, u64 len, bool write);
> > > -- 
> > > 2.34.1
> > > 
> 

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

* Re: [PATCH 12/18] drm/xe/eudebug: implement userptr_vma access
  2024-10-21 22:34       ` Matthew Brost
@ 2024-10-23 11:32         ` Hajda, Andrzej
  2024-10-24 16:06           ` Matthew Brost
  0 siblings, 1 reply; 40+ messages in thread
From: Hajda, Andrzej @ 2024-10-23 11:32 UTC (permalink / raw)
  To: Matthew Brost; +Cc: Mika Kuoppala, intel-xe, Maciej Patelczyk, Jonathan Cavitt

W dniu 22.10.2024 o 00:34, Matthew Brost pisze:
> On Mon, Oct 21, 2024 at 11:54:30AM +0200, Hajda, Andrzej wrote:
>> W dniu 20.10.2024 o 20:16, Matthew Brost pisze:
>>> On Tue, Oct 01, 2024 at 05:43:00PM +0300, Mika Kuoppala wrote:
>>>> From: Andrzej Hajda <andrzej.hajda@intel.com>
>>>> 
>>>> Debugger needs to read/write program's vmas including 
>>>> userptr_vma. Since hmm_range_fault is used to pin userptr 
>>>> vmas, it is possible to map those vmas from debugger context.
>>>> 
>>>> v2: pin pages vs notifier, move to vm.c (Matthew)
>>>> 
>>>> Signed-off-by: Andrzej Hajda <andrzej.hajda@intel.com>
>>>> Signed- off-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
>>>> Signed- off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com> 
>>>> Reviewed- by: Jonathan Cavitt <jonathan.cavitt@intel.com> --- 
>>>> drivers/ gpu/drm/xe/xe_eudebug.c |  2 +- drivers/gpu/drm/xe/ 
>>>> xe_vm.c      | 47 +++++++++++++++++++++++++++++++++ drivers/ 
>>>> gpu/drm/xe/xe_vm.h      |  3 +++ 3 files changed, 51 
>>>> insertions(+), 1 deletion(-)
>>>> 
>>>> diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/ 
>>>> drm/ xe/xe_eudebug.c index edad6d533d0b..b09d7414cfe3 100644 
>>>> --- a/ drivers/gpu/drm/xe/xe_eudebug.c +++ b/drivers/gpu/drm/ 
>>>> xe/ xe_eudebug.c @@ -3023,7 +3023,7 @@ static int 
>>>> xe_eudebug_vma_access(struct xe_vma *vma, u64 offset, return 
>>>> ret; } -	return -EINVAL; +	return 
>>>> xe_uvma_access(to_userptr_vma(vma), offset, buf, bytes, 
>>>> write); } static int xe_eudebug_vm_access(struct xe_vm *vm, 
>>>> u64 offset, diff --git a/drivers/gpu/drm/xe/xe_vm.c b/
>>>> drivers/ gpu/drm/xe/xe_vm.c index a836dfc5a86f..5f891e76993b
>>>> 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/
>>>> xe/xe_vm.c @@ -3421,3 +3421,50 @@ void
>>>> xe_vm_snapshot_free(struct xe_vm_snapshot *snap) }
>>>> kvfree(snap); } + +int xe_uvma_access(struct xe_userptr_vma
>>>> *uvma, u64 offset, + void *buf, u64 len, bool write) +{
>>> 
>>> Maybe dump question but are we overthinking this here?
>>> 
>>> Can we just use kthread_use_mm, copy_to_user, copy_from_user?
>>> 
>>> If not then my previous comments still apply here.
>> 
>> This function is called from debugger process context and 
>> kthread_use_mm is allowed only from kthread. Spawning kthread
>> just for this is an option but looks odd and suboptimal, could be
>> kind of last resort, or not?
>> 
>> Another options: 1. Keep reference to remote task in xe_userptr 
>> and use access_process_vm(up->task, ...).
>> 
> 
> I think remote refs are generally a bad idea but admittedly don't 
> fully understand what this would look like.
> 
>> 2. Pass xe_eudebug.target_task reference down from eudebug 
>> framework to this helper and use access_process_vm. Current call 
>> chain is: __xe_eudebug_vm_access - has access to 
>> xe_eudebug.target_task ->__vm_read_write --->xe_eudebug_vm_access 
>> ---->xe_eudebug_vm_access ----->xe_eudebug_vma_access ------
>>> xe_vm_userptr_access So to achieve this multiple changes are
>> required, but maybe it is valid path to go? One potential issue 
>> with 1 and 2 is that multiple UMD tests were failing when 
>> access_process_vm/access_remote_vm were used, they were not 
>> investigated as this approach was dropped due to different 
>> reasons.
>> 
>> 3. Continue approach from this patch, but with corrected page 
>> iterator of up->sg sg list[1]. This was nacked by you(?) [2] but
>> I have problem understanding why? I see lot of code in kernel 
>> mapping sg pages: linux$ git grep ' kmap.*sg' | wc -l
> 
> I looked here every example I found has mapping and accessing 1
> page at time not mapping 1 page and accessing many.
> 
>> 61 Is it incorrect? Or our case is different?
>> 
> 
> A sglist segments are dma-addresses (virtual address), thus every
> 4k in the segment can be a different physical page.

sglist is also list of pages and their lengths (in case of consecutive
pages, they are glued together), i.e. exactly what we need. And this is
done in xe_build_sg: it calls sg_alloc_table_from_pages_segment which
is documented as follows:
...
* Allocate and initialize an sg table from a list of pages. Contiguous
* ranges of the pages are squashed into a single scatterlist node up to the
* maximum size specified in @max_segment. A user may provide an offset at a
* start and a size of valid data in a buffer specified by the page array.
...
So sglist contains the same information as hmm_range->hmm_pfns[i] and 
little more.

So my concern is if mapping operation can destroy this info, but looking 
at the code this does not seems to be the case. For example 
iommu_dma_map_sg docs explicitly says about "preserve the original 
offsets and sizes for the caller".


> 
> i.e., look at this snippet:
> 
> +		void *ptr = kmap_local_page(sg_page(cur.sgl)) + cur.start; + + 
> cur_len = min(cur.size, cur.remaining); +		if (write) + memcpy(ptr, 
> buf, cur_len); +		else +			memcpy(buf, ptr, cur_len); + 
> kunmap_local(ptr);
> 
> If 'cur.start' > 4k, then you are potentially pointing to an 
> incorrect page and corrupting memory.

With added possibility to iterate over sgl pages in xe_res_cursor[1] it 
does not seems to be true.
Why? cur.start is limited by length of the segment (cur.sgl->length), if 
it happens to be more than 4k, it means sg_page(cur.sgl) points to 
consecutive pages and cur.start is correct.

> 
> Likewise if 'cur_len' > 4k, then you are potentially pointing to an 
> incorrect page and corrupting memory.

Again, in case of consecutive pages it should be in range.

Anyway if there is an issue with consecutive pages, which I am not aware 
of, we can always build sg list with segments pointing to 4K pages by
modyfing xe_build_sg to call sg_alloc_table_from_pages_segment with 4K 
max segment size.

[1]: 
https://lore.kernel.org/intel-xe/20241011-xe_res_cursor_add_page_iterator-v3-1-0f8b8d3ab021@intel.com/

Regards
Andrzej

> 
> This loop would have to be changed to something like below which 
> kmaps and accesses 1 page at a time...  for (xe_res_first_sg(up-
> >sg, offset, len, &cur); cur.remaining; xe_res_next(&cur, cur_len))
> { int segment_len; int remain;
> 
> cur_len = min(cur.size, cur.remaining); remain = cur_len;
> 
> for (i = 0; i < cur_len; i += segment_len) { phys_addr_t phys = 
> iommu_iova_to_phys(sg_dma_address(cur.sgl) + i + cur.start); struct 
> page *page = phys_to_page(phys); void *ptr = kmap_local_page(page); 
> int ptr_offset = offset & ~PAGE_MASK;
> 
> segment_len = min(remain, PAGE_SIZE - ptr_offset);
> 
> if (write) memcpy(ptr + ptr_offset, buf + i, segment_len); else 
> memcpy(buf + i, ptr + ptr_offset, segment_len); kunmap_local(ptr);
> 
> offset += segment_len; remain -= segment_len; }  buf += cur_len; }
> 
>> 4. As you suggested in [3](?), modify 
>> xe_hmm_userptr_populate_range to keep hmm_range.hmm_pfns(or sth 
>> similar) in xe_userptr and use it later (instead of up->sg) to 
>> iterate over pages.
>> 
> 
> Or just call hmm_range_fault directly here and operate on returned 
> pages directly.
> 
> BTW eventually all the userptr stuff is going to change and be
> based on GPU SVM [4]. Calling hmm_range_fault directly will always
> work though and likely the safest option.
> 
> Matt
> 
> [4] https://patchwork.freedesktop.org/patch/619809/? 
> series=137870&rev=2
> 
>> [1]: https://lore.kernel.org/intel-xe/20241011- 
>> xe_res_cursor_add_page_iterator-v3-1-0f8b8d3ab021@intel.com/ [2]: 
>> https://lore.kernel.org/intel-xe/Zw32fauoUmB6Iojk@DUT025- 
>> TGLU.fm.intel.com/ [3]: https://patchwork.freedesktop.org/ 
>> patch/617481/?series=136572&rev=2#comment_1126527
>> 
>> Regards Andrzej
>> 
>>> 
>>> Matt
>>> 
>>>> +	struct xe_vm *vm = xe_vma_vm(&uvma->vma); +	struct 
>>>> xe_userptr *up = &uvma->userptr; +	struct xe_res_cursor cur = 
>>>> {}; +	int cur_len, ret = 0; + +	while (true) { +
>>>> down_read(&vm-
>>>>> userptr.notifier_lock); +		if (!
>>>> xe_vma_userptr_check_repin(uvma)) +			break; + +
>>>> spin_lock(&vm-
>>>>> userptr.invalidated_lock); + list_del_init(&uvma- 
>>>>> userptr.invalidate_link); + spin_unlock(&vm- 
>>>>> userptr.invalidated_lock); + +		up_read(&vm- 
>>>>> userptr.notifier_lock); +		ret =
>>>> xe_vma_userptr_pin_pages(uvma); +		if (ret) +			return ret; 
>>>> +	} + +	if (!up->sg) { +		ret = -EINVAL; +		goto 
>>>> out_unlock_notifier; +	} + +	for (xe_res_first_sg(up->sg, 
>>>> offset, len, &cur); cur.remaining; +	     xe_res_next(&cur, 
>>>> cur_len)) { +		void *ptr = kmap_local_page(sg_page(cur.sgl))
>>>> + cur.start; + +		cur_len = min(cur.size, cur.remaining); +
>>>> if (write) +			memcpy(ptr, buf, cur_len); +		else +
>>>> memcpy(buf, ptr, cur_len); +		kunmap_local(ptr); +		buf +=
>>>> cur_len; +	} + ret = len; + +out_unlock_notifier: +
>>>> up_read(&vm-
>>>>> userptr.notifier_lock); +	return ret; +} diff --git a/ 
>>>>> drivers/
>>>> gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h index 
>>>> c864dba35e1d..99b9a9b011de 100644 --- a/drivers/gpu/drm/xe/ 
>>>> xe_vm.h +++ b/drivers/gpu/drm/xe/xe_vm.h @@ -281,3 +281,6 @@ 
>>>> struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm 
>>>> *vm); void xe_vm_snapshot_capture_delayed(struct 
>>>> xe_vm_snapshot *snap); void xe_vm_snapshot_print(struct 
>>>> xe_vm_snapshot *snap, struct drm_printer *p); void 
>>>> xe_vm_snapshot_free(struct xe_vm_snapshot *snap); + +int 
>>>> xe_uvma_access(struct xe_userptr_vma *uvma, u64 offset, + void 
>>>> *buf, u64 len, bool write); -- 2.34.1
>>>> 
>> 


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

* Re: [PATCH 12/18] drm/xe/eudebug: implement userptr_vma access
  2024-10-23 11:32         ` Hajda, Andrzej
@ 2024-10-24 16:06           ` Matthew Brost
  2024-10-25 13:20             ` Hajda, Andrzej
  0 siblings, 1 reply; 40+ messages in thread
From: Matthew Brost @ 2024-10-24 16:06 UTC (permalink / raw)
  To: Hajda, Andrzej; +Cc: Mika Kuoppala, intel-xe, Maciej Patelczyk, Jonathan Cavitt

On Wed, Oct 23, 2024 at 01:32:53PM +0200, Hajda, Andrzej wrote:
> W dniu 22.10.2024 o 00:34, Matthew Brost pisze:
> > On Mon, Oct 21, 2024 at 11:54:30AM +0200, Hajda, Andrzej wrote:
> > > W dniu 20.10.2024 o 20:16, Matthew Brost pisze:
> > > > On Tue, Oct 01, 2024 at 05:43:00PM +0300, Mika Kuoppala wrote:
> > > > > From: Andrzej Hajda <andrzej.hajda@intel.com>
> > > > > 
> > > > > Debugger needs to read/write program's vmas including
> > > > > userptr_vma. Since hmm_range_fault is used to pin userptr
> > > > > vmas, it is possible to map those vmas from debugger
> > > > > context.
> > > > > 
> > > > > v2: pin pages vs notifier, move to vm.c (Matthew)
> > > > > 
> > > > > Signed-off-by: Andrzej Hajda <andrzej.hajda@intel.com>
> > > > > Signed- off-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
> > > > > Signed- off-by: Mika Kuoppala
> > > > > <mika.kuoppala@linux.intel.com> Reviewed- by: Jonathan
> > > > > Cavitt <jonathan.cavitt@intel.com> --- drivers/
> > > > > gpu/drm/xe/xe_eudebug.c |  2 +- drivers/gpu/drm/xe/ xe_vm.c
> > > > > | 47 +++++++++++++++++++++++++++++++++ drivers/
> > > > > gpu/drm/xe/xe_vm.h      |  3 +++ 3 files changed, 51
> > > > > insertions(+), 1 deletion(-)
> > > > > 
> > > > > diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/
> > > > > drm/ xe/xe_eudebug.c index edad6d533d0b..b09d7414cfe3 100644
> > > > > --- a/ drivers/gpu/drm/xe/xe_eudebug.c +++
> > > > > b/drivers/gpu/drm/ xe/ xe_eudebug.c @@ -3023,7 +3023,7 @@
> > > > > static int xe_eudebug_vma_access(struct xe_vma *vma, u64
> > > > > offset, return ret; } -	return -EINVAL; +	return
> > > > > xe_uvma_access(to_userptr_vma(vma), offset, buf, bytes,
> > > > > write); } static int xe_eudebug_vm_access(struct xe_vm *vm,
> > > > > u64 offset, diff --git a/drivers/gpu/drm/xe/xe_vm.c b/
> > > > > drivers/ gpu/drm/xe/xe_vm.c index a836dfc5a86f..5f891e76993b
> > > > > 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/
> > > > > xe/xe_vm.c @@ -3421,3 +3421,50 @@ void
> > > > > xe_vm_snapshot_free(struct xe_vm_snapshot *snap) }
> > > > > kvfree(snap); } + +int xe_uvma_access(struct xe_userptr_vma
> > > > > *uvma, u64 offset, + void *buf, u64 len, bool write) +{
> > > > 
> > > > Maybe dump question but are we overthinking this here?
> > > > 
> > > > Can we just use kthread_use_mm, copy_to_user, copy_from_user?
> > > > 
> > > > If not then my previous comments still apply here.
> > > 
> > > This function is called from debugger process context and
> > > kthread_use_mm is allowed only from kthread. Spawning kthread
> > > just for this is an option but looks odd and suboptimal, could be
> > > kind of last resort, or not?
> > > 
> > > Another options: 1. Keep reference to remote task in xe_userptr and
> > > use access_process_vm(up->task, ...).
> > > 
> > 
> > I think remote refs are generally a bad idea but admittedly don't fully
> > understand what this would look like.
> > 
> > > 2. Pass xe_eudebug.target_task reference down from eudebug framework
> > > to this helper and use access_process_vm. Current call chain is:
> > > __xe_eudebug_vm_access - has access to xe_eudebug.target_task
> > > ->__vm_read_write --->xe_eudebug_vm_access ---->xe_eudebug_vm_access
> > > ----->xe_eudebug_vma_access ------
> > > > xe_vm_userptr_access So to achieve this multiple changes are
> > > required, but maybe it is valid path to go? One potential issue with
> > > 1 and 2 is that multiple UMD tests were failing when
> > > access_process_vm/access_remote_vm were used, they were not
> > > investigated as this approach was dropped due to different reasons.
> > > 
> > > 3. Continue approach from this patch, but with corrected page
> > > iterator of up->sg sg list[1]. This was nacked by you(?) [2] but
> > > I have problem understanding why? I see lot of code in kernel
> > > mapping sg pages: linux$ git grep ' kmap.*sg' | wc -l
> > 
> > I looked here every example I found has mapping and accessing 1
> > page at time not mapping 1 page and accessing many.
> > 
> > > 61 Is it incorrect? Or our case is different?
> > > 
> > 
> > A sglist segments are dma-addresses (virtual address), thus every
> > 4k in the segment can be a different physical page.
> 
> sglist is also list of pages and their lengths (in case of consecutive
> pages, they are glued together), i.e. exactly what we need. And this is
> done in xe_build_sg: it calls sg_alloc_table_from_pages_segment which
> is documented as follows:
> ...
> * Allocate and initialize an sg table from a list of pages. Contiguous
> * ranges of the pages are squashed into a single scatterlist node up to the
> * maximum size specified in @max_segment. A user may provide an offset at a
> * start and a size of valid data in a buffer specified by the page array.
> ...
> So sglist contains the same information as hmm_range->hmm_pfns[i] and little
> more.
> 
> So my concern is if mapping operation can destroy this info, but looking at
> the code this does not seems to be the case. For example iommu_dma_map_sg
> docs explicitly says about "preserve the original offsets and sizes for the
> caller".
> 
> 
> > 
> > i.e., look at this snippet:
> > 
> > +		void *ptr = kmap_local_page(sg_page(cur.sgl)) + cur.start; + +
> > cur_len = min(cur.size, cur.remaining); +		if (write) + memcpy(ptr, buf,
> > cur_len); +		else +			memcpy(buf, ptr, cur_len); + kunmap_local(ptr);
> > 
> > If 'cur.start' > 4k, then you are potentially pointing to an incorrect
> > page and corrupting memory.
> 
> With added possibility to iterate over sgl pages in xe_res_cursor[1] it does
> not seems to be true.
> Why? cur.start is limited by length of the segment (cur.sgl->length), if it
> happens to be more than 4k, it means sg_page(cur.sgl) points to consecutive
> pages and cur.start is correct.
> 

I suppose if the cursor is changed to walk the pages not the dma
address, yea i guess this would work. Still my much prefered way would
just call hmm_range_fault or optionally save off the pages in
xe_vma_userptr_pin_pages given at some point we will ditch SG tables for
userptr in favor of drm gpusvm which will be page based.

Matt

> > 
> > Likewise if 'cur_len' > 4k, then you are potentially pointing to an
> > incorrect page and corrupting memory.
> 
> Again, in case of consecutive pages it should be in range.
> 
> Anyway if there is an issue with consecutive pages, which I am not aware of,
> we can always build sg list with segments pointing to 4K pages by
> modyfing xe_build_sg to call sg_alloc_table_from_pages_segment with 4K max
> segment size.
> 
> [1]: https://lore.kernel.org/intel-xe/20241011-xe_res_cursor_add_page_iterator-v3-1-0f8b8d3ab021@intel.com/
> 
> Regards
> Andrzej
> 
> > 
> > This loop would have to be changed to something like below which kmaps
> > and accesses 1 page at a time...  for (xe_res_first_sg(up-
> > >sg, offset, len, &cur); cur.remaining; xe_res_next(&cur, cur_len))
> > { int segment_len; int remain;
> > 
> > cur_len = min(cur.size, cur.remaining); remain = cur_len;
> > 
> > for (i = 0; i < cur_len; i += segment_len) { phys_addr_t phys =
> > iommu_iova_to_phys(sg_dma_address(cur.sgl) + i + cur.start); struct page
> > *page = phys_to_page(phys); void *ptr = kmap_local_page(page); int
> > ptr_offset = offset & ~PAGE_MASK;
> > 
> > segment_len = min(remain, PAGE_SIZE - ptr_offset);
> > 
> > if (write) memcpy(ptr + ptr_offset, buf + i, segment_len); else
> > memcpy(buf + i, ptr + ptr_offset, segment_len); kunmap_local(ptr);
> > 
> > offset += segment_len; remain -= segment_len; }  buf += cur_len; }
> > 
> > > 4. As you suggested in [3](?), modify xe_hmm_userptr_populate_range
> > > to keep hmm_range.hmm_pfns(or sth similar) in xe_userptr and use it
> > > later (instead of up->sg) to iterate over pages.
> > > 
> > 
> > Or just call hmm_range_fault directly here and operate on returned pages
> > directly.
> > 
> > BTW eventually all the userptr stuff is going to change and be
> > based on GPU SVM [4]. Calling hmm_range_fault directly will always
> > work though and likely the safest option.
> > 
> > Matt
> > 
> > [4] https://patchwork.freedesktop.org/patch/619809/? series=137870&rev=2
> > 
> > > [1]: https://lore.kernel.org/intel-xe/20241011-
> > > xe_res_cursor_add_page_iterator-v3-1-0f8b8d3ab021@intel.com/ [2]:
> > > https://lore.kernel.org/intel-xe/Zw32fauoUmB6Iojk@DUT025-
> > > TGLU.fm.intel.com/ [3]: https://patchwork.freedesktop.org/
> > > patch/617481/?series=136572&rev=2#comment_1126527
> > > 
> > > Regards Andrzej
> > > 
> > > > 
> > > > Matt
> > > > 
> > > > > +	struct xe_vm *vm = xe_vma_vm(&uvma->vma); +	struct
> > > > > xe_userptr *up = &uvma->userptr; +	struct xe_res_cursor cur
> > > > > = {}; +	int cur_len, ret = 0; + +	while (true) { +
> > > > > down_read(&vm-
> > > > > > userptr.notifier_lock); +		if (!
> > > > > xe_vma_userptr_check_repin(uvma)) +			break; + +
> > > > > spin_lock(&vm-
> > > > > > userptr.invalidated_lock); + list_del_init(&uvma-
> > > > > > userptr.invalidate_link); + spin_unlock(&vm-
> > > > > > userptr.invalidated_lock); + +		up_read(&vm-
> > > > > > userptr.notifier_lock); +		ret =
> > > > > xe_vma_userptr_pin_pages(uvma); +		if (ret) +			return ret;
> > > > > +	} + +	if (!up->sg) { +		ret = -EINVAL; +		goto
> > > > > out_unlock_notifier; +	} + +	for (xe_res_first_sg(up->sg,
> > > > > offset, len, &cur); cur.remaining; +	     xe_res_next(&cur,
> > > > > cur_len)) { +		void *ptr = kmap_local_page(sg_page(cur.sgl))
> > > > > + cur.start; + +		cur_len = min(cur.size, cur.remaining); +
> > > > > if (write) +			memcpy(ptr, buf, cur_len); +		else +
> > > > > memcpy(buf, ptr, cur_len); +		kunmap_local(ptr); +		buf +=
> > > > > cur_len; +	} + ret = len; + +out_unlock_notifier: +
> > > > > up_read(&vm-
> > > > > > userptr.notifier_lock); +	return ret; +} diff --git a/
> > > > > > drivers/
> > > > > gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h index
> > > > > c864dba35e1d..99b9a9b011de 100644 --- a/drivers/gpu/drm/xe/
> > > > > xe_vm.h +++ b/drivers/gpu/drm/xe/xe_vm.h @@ -281,3 +281,6 @@
> > > > > struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm
> > > > > *vm); void xe_vm_snapshot_capture_delayed(struct
> > > > > xe_vm_snapshot *snap); void xe_vm_snapshot_print(struct
> > > > > xe_vm_snapshot *snap, struct drm_printer *p); void
> > > > > xe_vm_snapshot_free(struct xe_vm_snapshot *snap); + +int
> > > > > xe_uvma_access(struct xe_userptr_vma *uvma, u64 offset, +
> > > > > void *buf, u64 len, bool write); -- 2.34.1
> > > > > 
> > > 
> 

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

* Re: [PATCH 12/18] drm/xe/eudebug: implement userptr_vma access
  2024-10-24 16:06           ` Matthew Brost
@ 2024-10-25 13:20             ` Hajda, Andrzej
  2024-10-25 18:23               ` Matthew Brost
  0 siblings, 1 reply; 40+ messages in thread
From: Hajda, Andrzej @ 2024-10-25 13:20 UTC (permalink / raw)
  To: Matthew Brost; +Cc: Mika Kuoppala, intel-xe, Maciej Patelczyk, Jonathan Cavitt


W dniu 24.10.2024 o 18:06, Matthew Brost pisze:
> On Wed, Oct 23, 2024 at 01:32:53PM +0200, Hajda, Andrzej wrote:
>> W dniu 22.10.2024 o 00:34, Matthew Brost pisze:
>>> On Mon, Oct 21, 2024 at 11:54:30AM +0200, Hajda, Andrzej wrote:
>>>> W dniu 20.10.2024 o 20:16, Matthew Brost pisze:
>>>>> On Tue, Oct 01, 2024 at 05:43:00PM +0300, Mika Kuoppala wrote:
>>>>>> From: Andrzej Hajda <andrzej.hajda@intel.com>
>>>>>>
>>>>>> Debugger needs to read/write program's vmas including
>>>>>> userptr_vma. Since hmm_range_fault is used to pin userptr
>>>>>> vmas, it is possible to map those vmas from debugger
>>>>>> context.
>>>>>>
>>>>>> v2: pin pages vs notifier, move to vm.c (Matthew)
>>>>>>
>>>>>> Signed-off-by: Andrzej Hajda <andrzej.hajda@intel.com>
>>>>>> Signed- off-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
>>>>>> Signed- off-by: Mika Kuoppala
>>>>>> <mika.kuoppala@linux.intel.com> Reviewed- by: Jonathan
>>>>>> Cavitt <jonathan.cavitt@intel.com> --- drivers/
>>>>>> gpu/drm/xe/xe_eudebug.c |  2 +- drivers/gpu/drm/xe/ xe_vm.c
>>>>>> | 47 +++++++++++++++++++++++++++++++++ drivers/
>>>>>> gpu/drm/xe/xe_vm.h      |  3 +++ 3 files changed, 51
>>>>>> insertions(+), 1 deletion(-)
>>>>>>
>>>>>> diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/
>>>>>> drm/ xe/xe_eudebug.c index edad6d533d0b..b09d7414cfe3 100644
>>>>>> --- a/ drivers/gpu/drm/xe/xe_eudebug.c +++
>>>>>> b/drivers/gpu/drm/ xe/ xe_eudebug.c @@ -3023,7 +3023,7 @@
>>>>>> static int xe_eudebug_vma_access(struct xe_vma *vma, u64
>>>>>> offset, return ret; } -	return -EINVAL; +	return
>>>>>> xe_uvma_access(to_userptr_vma(vma), offset, buf, bytes,
>>>>>> write); } static int xe_eudebug_vm_access(struct xe_vm *vm,
>>>>>> u64 offset, diff --git a/drivers/gpu/drm/xe/xe_vm.c b/
>>>>>> drivers/ gpu/drm/xe/xe_vm.c index a836dfc5a86f..5f891e76993b
>>>>>> 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/
>>>>>> xe/xe_vm.c @@ -3421,3 +3421,50 @@ void
>>>>>> xe_vm_snapshot_free(struct xe_vm_snapshot *snap) }
>>>>>> kvfree(snap); } + +int xe_uvma_access(struct xe_userptr_vma
>>>>>> *uvma, u64 offset, + void *buf, u64 len, bool write) +{
>>>>> Maybe dump question but are we overthinking this here?
>>>>>
>>>>> Can we just use kthread_use_mm, copy_to_user, copy_from_user?
>>>>>
>>>>> If not then my previous comments still apply here.
>>>> This function is called from debugger process context and
>>>> kthread_use_mm is allowed only from kthread. Spawning kthread
>>>> just for this is an option but looks odd and suboptimal, could be
>>>> kind of last resort, or not?
>>>>
>>>> Another options: 1. Keep reference to remote task in xe_userptr and
>>>> use access_process_vm(up->task, ...).
>>>>
>>> I think remote refs are generally a bad idea but admittedly don't fully
>>> understand what this would look like.
>>>
>>>> 2. Pass xe_eudebug.target_task reference down from eudebug framework
>>>> to this helper and use access_process_vm. Current call chain is:
>>>> __xe_eudebug_vm_access - has access to xe_eudebug.target_task
>>>> ->__vm_read_write --->xe_eudebug_vm_access ---->xe_eudebug_vm_access
>>>> ----->xe_eudebug_vma_access ------
>>>>> xe_vm_userptr_access So to achieve this multiple changes are
>>>> required, but maybe it is valid path to go? One potential issue with
>>>> 1 and 2 is that multiple UMD tests were failing when
>>>> access_process_vm/access_remote_vm were used, they were not
>>>> investigated as this approach was dropped due to different reasons.
>>>>
>>>> 3. Continue approach from this patch, but with corrected page
>>>> iterator of up->sg sg list[1]. This was nacked by you(?) [2] but
>>>> I have problem understanding why? I see lot of code in kernel
>>>> mapping sg pages: linux$ git grep ' kmap.*sg' | wc -l
>>> I looked here every example I found has mapping and accessing 1
>>> page at time not mapping 1 page and accessing many.
>>>
>>>> 61 Is it incorrect? Or our case is different?
>>>>
>>> A sglist segments are dma-addresses (virtual address), thus every
>>> 4k in the segment can be a different physical page.
>> sglist is also list of pages and their lengths (in case of consecutive
>> pages, they are glued together), i.e. exactly what we need. And this is
>> done in xe_build_sg: it calls sg_alloc_table_from_pages_segment which
>> is documented as follows:
>> ...
>> * Allocate and initialize an sg table from a list of pages. Contiguous
>> * ranges of the pages are squashed into a single scatterlist node up to the
>> * maximum size specified in @max_segment. A user may provide an offset at a
>> * start and a size of valid data in a buffer specified by the page array.
>> ...
>> So sglist contains the same information as hmm_range->hmm_pfns[i] and little
>> more.
>>
>> So my concern is if mapping operation can destroy this info, but looking at
>> the code this does not seems to be the case. For example iommu_dma_map_sg
>> docs explicitly says about "preserve the original offsets and sizes for the
>> caller".
>>
>>
>>> i.e., look at this snippet:
>>>
>>> +		void *ptr = kmap_local_page(sg_page(cur.sgl)) + cur.start; + +
>>> cur_len = min(cur.size, cur.remaining); +		if (write) + memcpy(ptr, buf,
>>> cur_len); +		else +			memcpy(buf, ptr, cur_len); + kunmap_local(ptr);
>>>
>>> If 'cur.start' > 4k, then you are potentially pointing to an incorrect
>>> page and corrupting memory.
>> With added possibility to iterate over sgl pages in xe_res_cursor[1] it does
>> not seems to be true.
>> Why? cur.start is limited by length of the segment (cur.sgl->length), if it
>> happens to be more than 4k, it means sg_page(cur.sgl) points to consecutive
>> pages and cur.start is correct.
>>
> I suppose if the cursor is changed to walk the pages not the dma
> address, yea i guess this would work. Still my much prefered way would
> just call hmm_range_fault or optionally save off the pages in
> xe_vma_userptr_pin_pages given at some point we will ditch SG tables for
> userptr in favor of drm gpusvm which will be page based.

Both alternatives seems for me suboptimal, as their result is what we 
have already in sg table, for a price of extra call (hmm_range_fault) 
and extra allocations (both). Most important they will complicate the 
code without clear benefit.

I will try to implement version with hmm_range_fault but I am still 
confused why we need to complicate things.


Regards

Andrzej


>
> Matt
>
>>> Likewise if 'cur_len' > 4k, then you are potentially pointing to an
>>> incorrect page and corrupting memory.
>> Again, in case of consecutive pages it should be in range.
>>
>> Anyway if there is an issue with consecutive pages, which I am not aware of,
>> we can always build sg list with segments pointing to 4K pages by
>> modyfing xe_build_sg to call sg_alloc_table_from_pages_segment with 4K max
>> segment size.
>>
>> [1]: https://lore.kernel.org/intel-xe/20241011-xe_res_cursor_add_page_iterator-v3-1-0f8b8d3ab021@intel.com/
>>
>> Regards
>> Andrzej
>>
>>> This loop would have to be changed to something like below which kmaps
>>> and accesses 1 page at a time...  for (xe_res_first_sg(up-
>>>> sg, offset, len, &cur); cur.remaining; xe_res_next(&cur, cur_len))
>>> { int segment_len; int remain;
>>>
>>> cur_len = min(cur.size, cur.remaining); remain = cur_len;
>>>
>>> for (i = 0; i < cur_len; i += segment_len) { phys_addr_t phys =
>>> iommu_iova_to_phys(sg_dma_address(cur.sgl) + i + cur.start); struct page
>>> *page = phys_to_page(phys); void *ptr = kmap_local_page(page); int
>>> ptr_offset = offset & ~PAGE_MASK;
>>>
>>> segment_len = min(remain, PAGE_SIZE - ptr_offset);
>>>
>>> if (write) memcpy(ptr + ptr_offset, buf + i, segment_len); else
>>> memcpy(buf + i, ptr + ptr_offset, segment_len); kunmap_local(ptr);
>>>
>>> offset += segment_len; remain -= segment_len; }  buf += cur_len; }
>>>
>>>> 4. As you suggested in [3](?), modify xe_hmm_userptr_populate_range
>>>> to keep hmm_range.hmm_pfns(or sth similar) in xe_userptr and use it
>>>> later (instead of up->sg) to iterate over pages.
>>>>
>>> Or just call hmm_range_fault directly here and operate on returned pages
>>> directly.
>>>
>>> BTW eventually all the userptr stuff is going to change and be
>>> based on GPU SVM [4]. Calling hmm_range_fault directly will always
>>> work though and likely the safest option.
>>>
>>> Matt
>>>
>>> [4] https://patchwork.freedesktop.org/patch/619809/? series=137870&rev=2
>>>
>>>> [1]: https://lore.kernel.org/intel-xe/20241011-
>>>> xe_res_cursor_add_page_iterator-v3-1-0f8b8d3ab021@intel.com/ [2]:
>>>> https://lore.kernel.org/intel-xe/Zw32fauoUmB6Iojk@DUT025-
>>>> TGLU.fm.intel.com/ [3]: https://patchwork.freedesktop.org/
>>>> patch/617481/?series=136572&rev=2#comment_1126527
>>>>
>>>> Regards Andrzej
>>>>
>>>>> Matt
>>>>>
>>>>>> +	struct xe_vm *vm = xe_vma_vm(&uvma->vma); +	struct
>>>>>> xe_userptr *up = &uvma->userptr; +	struct xe_res_cursor cur
>>>>>> = {}; +	int cur_len, ret = 0; + +	while (true) { +
>>>>>> down_read(&vm-
>>>>>>> userptr.notifier_lock); +		if (!
>>>>>> xe_vma_userptr_check_repin(uvma)) +			break; + +
>>>>>> spin_lock(&vm-
>>>>>>> userptr.invalidated_lock); + list_del_init(&uvma-
>>>>>>> userptr.invalidate_link); + spin_unlock(&vm-
>>>>>>> userptr.invalidated_lock); + +		up_read(&vm-
>>>>>>> userptr.notifier_lock); +		ret =
>>>>>> xe_vma_userptr_pin_pages(uvma); +		if (ret) +			return ret;
>>>>>> +	} + +	if (!up->sg) { +		ret = -EINVAL; +		goto
>>>>>> out_unlock_notifier; +	} + +	for (xe_res_first_sg(up->sg,
>>>>>> offset, len, &cur); cur.remaining; +	     xe_res_next(&cur,
>>>>>> cur_len)) { +		void *ptr = kmap_local_page(sg_page(cur.sgl))
>>>>>> + cur.start; + +		cur_len = min(cur.size, cur.remaining); +
>>>>>> if (write) +			memcpy(ptr, buf, cur_len); +		else +
>>>>>> memcpy(buf, ptr, cur_len); +		kunmap_local(ptr); +		buf +=
>>>>>> cur_len; +	} + ret = len; + +out_unlock_notifier: +
>>>>>> up_read(&vm-
>>>>>>> userptr.notifier_lock); +	return ret; +} diff --git a/
>>>>>>> drivers/
>>>>>> gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h index
>>>>>> c864dba35e1d..99b9a9b011de 100644 --- a/drivers/gpu/drm/xe/
>>>>>> xe_vm.h +++ b/drivers/gpu/drm/xe/xe_vm.h @@ -281,3 +281,6 @@
>>>>>> struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm
>>>>>> *vm); void xe_vm_snapshot_capture_delayed(struct
>>>>>> xe_vm_snapshot *snap); void xe_vm_snapshot_print(struct
>>>>>> xe_vm_snapshot *snap, struct drm_printer *p); void
>>>>>> xe_vm_snapshot_free(struct xe_vm_snapshot *snap); + +int
>>>>>> xe_uvma_access(struct xe_userptr_vma *uvma, u64 offset, +
>>>>>> void *buf, u64 len, bool write); -- 2.34.1
>>>>>>

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

* Re: [PATCH 12/18] drm/xe/eudebug: implement userptr_vma access
  2024-10-25 13:20             ` Hajda, Andrzej
@ 2024-10-25 18:23               ` Matthew Brost
  2024-10-28 16:19                 ` [PATCH 1/2] drm/xe: keep list of system pages in xe_userptr Andrzej Hajda
  2024-10-28 16:19                 ` [PATCH 2/2] drm/xe/eudebug: implement userptr_vma access Andrzej Hajda
  0 siblings, 2 replies; 40+ messages in thread
From: Matthew Brost @ 2024-10-25 18:23 UTC (permalink / raw)
  To: Hajda, Andrzej; +Cc: Mika Kuoppala, intel-xe, Maciej Patelczyk, Jonathan Cavitt

On Fri, Oct 25, 2024 at 03:20:18PM +0200, Hajda, Andrzej wrote:
> 
> W dniu 24.10.2024 o 18:06, Matthew Brost pisze:
> > On Wed, Oct 23, 2024 at 01:32:53PM +0200, Hajda, Andrzej wrote:
> > > W dniu 22.10.2024 o 00:34, Matthew Brost pisze:
> > > > On Mon, Oct 21, 2024 at 11:54:30AM +0200, Hajda, Andrzej wrote:
> > > > > W dniu 20.10.2024 o 20:16, Matthew Brost pisze:
> > > > > > On Tue, Oct 01, 2024 at 05:43:00PM +0300, Mika Kuoppala wrote:
> > > > > > > From: Andrzej Hajda <andrzej.hajda@intel.com>
> > > > > > > 
> > > > > > > Debugger needs to read/write program's vmas including
> > > > > > > userptr_vma. Since hmm_range_fault is used to pin userptr
> > > > > > > vmas, it is possible to map those vmas from debugger
> > > > > > > context.
> > > > > > > 
> > > > > > > v2: pin pages vs notifier, move to vm.c (Matthew)
> > > > > > > 
> > > > > > > Signed-off-by: Andrzej Hajda <andrzej.hajda@intel.com>
> > > > > > > Signed- off-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
> > > > > > > Signed- off-by: Mika Kuoppala
> > > > > > > <mika.kuoppala@linux.intel.com> Reviewed- by: Jonathan
> > > > > > > Cavitt <jonathan.cavitt@intel.com> --- drivers/
> > > > > > > gpu/drm/xe/xe_eudebug.c |  2 +- drivers/gpu/drm/xe/ xe_vm.c
> > > > > > > | 47 +++++++++++++++++++++++++++++++++ drivers/
> > > > > > > gpu/drm/xe/xe_vm.h      |  3 +++ 3 files changed, 51
> > > > > > > insertions(+), 1 deletion(-)
> > > > > > > 
> > > > > > > diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/
> > > > > > > drm/ xe/xe_eudebug.c index edad6d533d0b..b09d7414cfe3 100644
> > > > > > > --- a/ drivers/gpu/drm/xe/xe_eudebug.c +++
> > > > > > > b/drivers/gpu/drm/ xe/ xe_eudebug.c @@ -3023,7 +3023,7 @@
> > > > > > > static int xe_eudebug_vma_access(struct xe_vma *vma, u64
> > > > > > > offset, return ret; } -	return -EINVAL; +	return
> > > > > > > xe_uvma_access(to_userptr_vma(vma), offset, buf, bytes,
> > > > > > > write); } static int xe_eudebug_vm_access(struct xe_vm *vm,
> > > > > > > u64 offset, diff --git a/drivers/gpu/drm/xe/xe_vm.c b/
> > > > > > > drivers/ gpu/drm/xe/xe_vm.c index a836dfc5a86f..5f891e76993b
> > > > > > > 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/
> > > > > > > xe/xe_vm.c @@ -3421,3 +3421,50 @@ void
> > > > > > > xe_vm_snapshot_free(struct xe_vm_snapshot *snap) }
> > > > > > > kvfree(snap); } + +int xe_uvma_access(struct xe_userptr_vma
> > > > > > > *uvma, u64 offset, + void *buf, u64 len, bool write) +{
> > > > > > Maybe dump question but are we overthinking this here?
> > > > > > 
> > > > > > Can we just use kthread_use_mm, copy_to_user, copy_from_user?
> > > > > > 
> > > > > > If not then my previous comments still apply here.
> > > > > This function is called from debugger process context and
> > > > > kthread_use_mm is allowed only from kthread. Spawning kthread
> > > > > just for this is an option but looks odd and suboptimal, could be
> > > > > kind of last resort, or not?
> > > > > 
> > > > > Another options: 1. Keep reference to remote task in xe_userptr and
> > > > > use access_process_vm(up->task, ...).
> > > > > 
> > > > I think remote refs are generally a bad idea but admittedly don't fully
> > > > understand what this would look like.
> > > > 
> > > > > 2. Pass xe_eudebug.target_task reference down from eudebug framework
> > > > > to this helper and use access_process_vm. Current call chain is:
> > > > > __xe_eudebug_vm_access - has access to xe_eudebug.target_task
> > > > > ->__vm_read_write --->xe_eudebug_vm_access ---->xe_eudebug_vm_access
> > > > > ----->xe_eudebug_vma_access ------
> > > > > > xe_vm_userptr_access So to achieve this multiple changes are
> > > > > required, but maybe it is valid path to go? One potential issue with
> > > > > 1 and 2 is that multiple UMD tests were failing when
> > > > > access_process_vm/access_remote_vm were used, they were not
> > > > > investigated as this approach was dropped due to different reasons.
> > > > > 
> > > > > 3. Continue approach from this patch, but with corrected page
> > > > > iterator of up->sg sg list[1]. This was nacked by you(?) [2] but
> > > > > I have problem understanding why? I see lot of code in kernel
> > > > > mapping sg pages: linux$ git grep ' kmap.*sg' | wc -l
> > > > I looked here every example I found has mapping and accessing 1
> > > > page at time not mapping 1 page and accessing many.
> > > > 
> > > > > 61 Is it incorrect? Or our case is different?
> > > > > 
> > > > A sglist segments are dma-addresses (virtual address), thus every
> > > > 4k in the segment can be a different physical page.
> > > sglist is also list of pages and their lengths (in case of consecutive
> > > pages, they are glued together), i.e. exactly what we need. And this is
> > > done in xe_build_sg: it calls sg_alloc_table_from_pages_segment which
> > > is documented as follows:
> > > ...
> > > * Allocate and initialize an sg table from a list of pages. Contiguous
> > > * ranges of the pages are squashed into a single scatterlist node up to the
> > > * maximum size specified in @max_segment. A user may provide an offset at a
> > > * start and a size of valid data in a buffer specified by the page array.
> > > ...
> > > So sglist contains the same information as hmm_range->hmm_pfns[i] and little
> > > more.
> > > 
> > > So my concern is if mapping operation can destroy this info, but looking at
> > > the code this does not seems to be the case. For example iommu_dma_map_sg
> > > docs explicitly says about "preserve the original offsets and sizes for the
> > > caller".
> > > 
> > > 
> > > > i.e., look at this snippet:
> > > > 
> > > > +		void *ptr = kmap_local_page(sg_page(cur.sgl)) + cur.start; + +
> > > > cur_len = min(cur.size, cur.remaining); +		if (write) + memcpy(ptr, buf,
> > > > cur_len); +		else +			memcpy(buf, ptr, cur_len); + kunmap_local(ptr);
> > > > 
> > > > If 'cur.start' > 4k, then you are potentially pointing to an incorrect
> > > > page and corrupting memory.
> > > With added possibility to iterate over sgl pages in xe_res_cursor[1] it does
> > > not seems to be true.
> > > Why? cur.start is limited by length of the segment (cur.sgl->length), if it
> > > happens to be more than 4k, it means sg_page(cur.sgl) points to consecutive
> > > pages and cur.start is correct.
> > > 
> > I suppose if the cursor is changed to walk the pages not the dma
> > address, yea i guess this would work. Still my much prefered way would
> > just call hmm_range_fault or optionally save off the pages in
> > xe_vma_userptr_pin_pages given at some point we will ditch SG tables for
> > userptr in favor of drm gpusvm which will be page based.
> 
> Both alternatives seems for me suboptimal, as their result is what we have
> already in sg table, for a price of extra call (hmm_range_fault) and extra
> allocations (both). Most important they will complicate the code without
> clear benefit.
> 

Again the SG is going to eventually be dropped in favor of a page array
in GPU SVM. Also the general consensus among the Linux community is SG
tables are terrible and wouldn't be surprised if eventually these get
pulled out / deprecated in the kernel entirely. Fine if you want use a
SG table I guess as we will just rewrite this once GPU SVM based userptr
lands.

> I will try to implement version with hmm_range_fault but I am still confused
> why we need to complicate things.
> 

Correctness vs convenience...

It is a bit unfortunate our hmm_range_fault wrapper is built around a VMA
argument - I suggested a more generic interface there to this easier in
reviews but that feedback was ignored.

Matt

> 
> Regards
> 
> Andrzej
> 
> 
> > 
> > Matt
> > 
> > > > Likewise if 'cur_len' > 4k, then you are potentially pointing to an
> > > > incorrect page and corrupting memory.
> > > Again, in case of consecutive pages it should be in range.
> > > 
> > > Anyway if there is an issue with consecutive pages, which I am not aware of,
> > > we can always build sg list with segments pointing to 4K pages by
> > > modyfing xe_build_sg to call sg_alloc_table_from_pages_segment with 4K max
> > > segment size.
> > > 
> > > [1]: https://lore.kernel.org/intel-xe/20241011-xe_res_cursor_add_page_iterator-v3-1-0f8b8d3ab021@intel.com/
> > > 
> > > Regards
> > > Andrzej
> > > 
> > > > This loop would have to be changed to something like below which kmaps
> > > > and accesses 1 page at a time...  for (xe_res_first_sg(up-
> > > > > sg, offset, len, &cur); cur.remaining; xe_res_next(&cur, cur_len))
> > > > { int segment_len; int remain;
> > > > 
> > > > cur_len = min(cur.size, cur.remaining); remain = cur_len;
> > > > 
> > > > for (i = 0; i < cur_len; i += segment_len) { phys_addr_t phys =
> > > > iommu_iova_to_phys(sg_dma_address(cur.sgl) + i + cur.start); struct page
> > > > *page = phys_to_page(phys); void *ptr = kmap_local_page(page); int
> > > > ptr_offset = offset & ~PAGE_MASK;
> > > > 
> > > > segment_len = min(remain, PAGE_SIZE - ptr_offset);
> > > > 
> > > > if (write) memcpy(ptr + ptr_offset, buf + i, segment_len); else
> > > > memcpy(buf + i, ptr + ptr_offset, segment_len); kunmap_local(ptr);
> > > > 
> > > > offset += segment_len; remain -= segment_len; }  buf += cur_len; }
> > > > 
> > > > > 4. As you suggested in [3](?), modify xe_hmm_userptr_populate_range
> > > > > to keep hmm_range.hmm_pfns(or sth similar) in xe_userptr and use it
> > > > > later (instead of up->sg) to iterate over pages.
> > > > > 
> > > > Or just call hmm_range_fault directly here and operate on returned pages
> > > > directly.
> > > > 
> > > > BTW eventually all the userptr stuff is going to change and be
> > > > based on GPU SVM [4]. Calling hmm_range_fault directly will always
> > > > work though and likely the safest option.
> > > > 
> > > > Matt
> > > > 
> > > > [4] https://patchwork.freedesktop.org/patch/619809/? series=137870&rev=2
> > > > 
> > > > > [1]: https://lore.kernel.org/intel-xe/20241011-
> > > > > xe_res_cursor_add_page_iterator-v3-1-0f8b8d3ab021@intel.com/ [2]:
> > > > > https://lore.kernel.org/intel-xe/Zw32fauoUmB6Iojk@DUT025-
> > > > > TGLU.fm.intel.com/ [3]: https://patchwork.freedesktop.org/
> > > > > patch/617481/?series=136572&rev=2#comment_1126527
> > > > > 
> > > > > Regards Andrzej
> > > > > 
> > > > > > Matt
> > > > > > 
> > > > > > > +	struct xe_vm *vm = xe_vma_vm(&uvma->vma); +	struct
> > > > > > > xe_userptr *up = &uvma->userptr; +	struct xe_res_cursor cur
> > > > > > > = {}; +	int cur_len, ret = 0; + +	while (true) { +
> > > > > > > down_read(&vm-
> > > > > > > > userptr.notifier_lock); +		if (!
> > > > > > > xe_vma_userptr_check_repin(uvma)) +			break; + +
> > > > > > > spin_lock(&vm-
> > > > > > > > userptr.invalidated_lock); + list_del_init(&uvma-
> > > > > > > > userptr.invalidate_link); + spin_unlock(&vm-
> > > > > > > > userptr.invalidated_lock); + +		up_read(&vm-
> > > > > > > > userptr.notifier_lock); +		ret =
> > > > > > > xe_vma_userptr_pin_pages(uvma); +		if (ret) +			return ret;
> > > > > > > +	} + +	if (!up->sg) { +		ret = -EINVAL; +		goto
> > > > > > > out_unlock_notifier; +	} + +	for (xe_res_first_sg(up->sg,
> > > > > > > offset, len, &cur); cur.remaining; +	     xe_res_next(&cur,
> > > > > > > cur_len)) { +		void *ptr = kmap_local_page(sg_page(cur.sgl))
> > > > > > > + cur.start; + +		cur_len = min(cur.size, cur.remaining); +
> > > > > > > if (write) +			memcpy(ptr, buf, cur_len); +		else +
> > > > > > > memcpy(buf, ptr, cur_len); +		kunmap_local(ptr); +		buf +=
> > > > > > > cur_len; +	} + ret = len; + +out_unlock_notifier: +
> > > > > > > up_read(&vm-
> > > > > > > > userptr.notifier_lock); +	return ret; +} diff --git a/
> > > > > > > > drivers/
> > > > > > > gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h index
> > > > > > > c864dba35e1d..99b9a9b011de 100644 --- a/drivers/gpu/drm/xe/
> > > > > > > xe_vm.h +++ b/drivers/gpu/drm/xe/xe_vm.h @@ -281,3 +281,6 @@
> > > > > > > struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm
> > > > > > > *vm); void xe_vm_snapshot_capture_delayed(struct
> > > > > > > xe_vm_snapshot *snap); void xe_vm_snapshot_print(struct
> > > > > > > xe_vm_snapshot *snap, struct drm_printer *p); void
> > > > > > > xe_vm_snapshot_free(struct xe_vm_snapshot *snap); + +int
> > > > > > > xe_uvma_access(struct xe_userptr_vma *uvma, u64 offset, +
> > > > > > > void *buf, u64 len, bool write); -- 2.34.1
> > > > > > > 

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

* [PATCH 1/2] drm/xe: keep list of system pages in xe_userptr
  2024-10-25 18:23               ` Matthew Brost
@ 2024-10-28 16:19                 ` Andrzej Hajda
  2024-10-28 16:19                 ` [PATCH 2/2] drm/xe/eudebug: implement userptr_vma access Andrzej Hajda
  1 sibling, 0 replies; 40+ messages in thread
From: Andrzej Hajda @ 2024-10-28 16:19 UTC (permalink / raw)
  To: Matthew Brost; +Cc: Andrzej Hajda, intel-xe, Mika Kuoppala, Jonathan Cavitt

In case of accessing data provided by userptr in the driver we need to
have list of corresponding pages. This list is created by
xe_hmm_userptr_populate_range and stored in xe_userptr.sgl sg list.
Since we are not allowed to get pages from sg list, let's store them in
separate field.

Signed-off-by: Andrzej Hajda <andrzej.hajda@intel.com>
---
 drivers/gpu/drm/xe/xe_hmm.c      | 52 ++++++++++++++++----------------
 drivers/gpu/drm/xe/xe_vm_types.h |  2 ++
 2 files changed, 28 insertions(+), 26 deletions(-)

diff --git a/drivers/gpu/drm/xe/xe_hmm.c b/drivers/gpu/drm/xe/xe_hmm.c
index 2c32dc46f7d4..6fc21e04ef2e 100644
--- a/drivers/gpu/drm/xe/xe_hmm.c
+++ b/drivers/gpu/drm/xe/xe_hmm.c
@@ -44,13 +44,13 @@ static void xe_mark_range_accessed(struct hmm_range *range, bool write)
 }
 
 /*
- * xe_build_sg() - build a scatter gather table for all the physical pages/pfn
- * in a hmm_range. dma-map pages if necessary. dma-address is save in sg table
+ * xe_build_sg() - build a scatter gather table for given physical pages
+ * and perform dma-map. dma-address is saved in sg table
  * and will be used to program GPU page table later.
  *
  * @xe: the xe device who will access the dma-address in sg table
- * @range: the hmm range that we build the sg table from. range->hmm_pfns[]
- * has the pfn numbers of pages that back up this hmm address range.
+ * @pages: array of page pointers
+ * @npages: number of entries in @pages
  * @st: pointer to the sg table.
  * @write: whether we write to this range. This decides dma map direction
  * for system pages. If write we map it bi-diretional; otherwise
@@ -77,38 +77,22 @@ static void xe_mark_range_accessed(struct hmm_range *range, bool write)
  *
  * Returns 0 if successful; -ENOMEM if fails to allocate memory
  */
-static int xe_build_sg(struct xe_device *xe, struct hmm_range *range,
+static int xe_build_sg(struct xe_device *xe, struct page **pages, u64 npages,
 		       struct sg_table *st, bool write)
 {
 	struct device *dev = xe->drm.dev;
-	struct page **pages;
-	u64 i, npages;
 	int ret;
 
-	npages = xe_npages_in_range(range->start, range->end);
-	pages = kvmalloc_array(npages, sizeof(*pages), GFP_KERNEL);
-	if (!pages)
-		return -ENOMEM;
-
-	for (i = 0; i < npages; i++) {
-		pages[i] = hmm_pfn_to_page(range->hmm_pfns[i]);
-		xe_assert(xe, !is_device_private_page(pages[i]));
-	}
-
 	ret = sg_alloc_table_from_pages_segment(st, pages, npages, 0, npages << PAGE_SHIFT,
 						xe_sg_segment_size(dev), GFP_KERNEL);
 	if (ret)
-		goto free_pages;
+		return ret;
 
 	ret = dma_map_sgtable(dev, st, write ? DMA_BIDIRECTIONAL : DMA_TO_DEVICE,
 			      DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_NO_KERNEL_MAPPING);
-	if (ret) {
+	if (ret)
 		sg_free_table(st);
-		st = NULL;
-	}
 
-free_pages:
-	kvfree(pages);
 	return ret;
 }
 
@@ -136,6 +120,8 @@ void xe_hmm_userptr_free_sg(struct xe_userptr_vma *uvma)
 
 	sg_free_table(userptr->sg);
 	userptr->sg = NULL;
+	kvfree(userptr->pages);
+	userptr->pages = NULL;
 }
 
 /**
@@ -175,7 +161,7 @@ int xe_hmm_userptr_populate_range(struct xe_userptr_vma *uvma,
 	struct hmm_range hmm_range;
 	bool write = !xe_vma_read_only(vma);
 	unsigned long notifier_seq;
-	u64 npages;
+	u64 i, npages;
 	int ret;
 
 	userptr = &uvma->userptr;
@@ -238,9 +224,23 @@ int xe_hmm_userptr_populate_range(struct xe_userptr_vma *uvma,
 	if (ret)
 		goto free_pfns;
 
-	ret = xe_build_sg(vm->xe, &hmm_range, &userptr->sgt, write);
-	if (ret)
+	userptr->pages = kvmalloc_array(npages, sizeof(*userptr->pages), GFP_KERNEL);
+	if (!userptr->pages) {
+		ret = -ENOMEM;
+		goto free_pfns;
+	}
+
+	for (i = 0; i < npages; i++) {
+		userptr->pages[i] = hmm_pfn_to_page(hmm_range.hmm_pfns[i]);
+		xe_assert(vm->xe, !is_device_private_page(userptr->pages[i]));
+	}
+
+	ret = xe_build_sg(vm->xe, userptr->pages, npages, &userptr->sgt, write);
+	if (ret) {
+		kvfree(userptr->pages);
+		userptr->pages = NULL;
 		goto free_pfns;
+	}
 
 	xe_mark_range_accessed(&hmm_range, write);
 	userptr->sg = &userptr->sgt;
diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_types.h
index 557b047ebdd7..f1ec3925dea0 100644
--- a/drivers/gpu/drm/xe/xe_vm_types.h
+++ b/drivers/gpu/drm/xe/xe_vm_types.h
@@ -53,6 +53,8 @@ struct xe_userptr {
 	 * @notifier: MMU notifier for user pointer (invalidation call back)
 	 */
 	struct mmu_interval_notifier notifier;
+	/** @pages: pointer to array of pointers to corresponding pages */
+	struct page **pages;
 	/** @sgt: storage for a scatter gather table */
 	struct sg_table sgt;
 	/** @sg: allocated scatter gather table */
-- 
2.34.1


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

* [PATCH 2/2] drm/xe/eudebug: implement userptr_vma access
  2024-10-25 18:23               ` Matthew Brost
  2024-10-28 16:19                 ` [PATCH 1/2] drm/xe: keep list of system pages in xe_userptr Andrzej Hajda
@ 2024-10-28 16:19                 ` Andrzej Hajda
  2024-10-28 16:55                   ` Hajda, Andrzej
  1 sibling, 1 reply; 40+ messages in thread
From: Andrzej Hajda @ 2024-10-28 16:19 UTC (permalink / raw)
  To: Matthew Brost; +Cc: Andrzej Hajda, intel-xe, Mika Kuoppala, Jonathan Cavitt

Debugger needs to read/write program's vmas including userptr_vma.
Since hmm_range_fault is used to pin userptr vmas, it is possible
to map those vmas from debugger context.

v2: pin pages vs notifier, move to vm.c (Matthew)
v3: - iterate over system pages instead of DMA, fixes work with iommu enabled
    - s/xe_uvma_access/xe_vm_uvma_access/ (Matt)
v4: use xe_userptr->pages, instead of sg to access pages (Matt)

Signed-off-by: Andrzej Hajda <andrzej.hajda@intel.com>
---
 drivers/gpu/drm/xe/xe_eudebug.c |  2 +-
 drivers/gpu/drm/xe/xe_vm.c      | 52 +++++++++++++++++++++++++++++++++
 drivers/gpu/drm/xe/xe_vm.h      |  3 ++
 3 files changed, 56 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index 1bff0a2cfaa1..569c8d0b2ef8 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -3049,7 +3049,7 @@ static int xe_eudebug_vma_access(struct xe_vma *vma, u64 offset,
 		return ret;
 	}
 
-	return -EINVAL;
+	return xe_vm_userptr_access(to_userptr_vma(vma), offset, buf, bytes, write);
 }
 
 static int xe_eudebug_vm_access(struct xe_vm *vm, u64 offset,
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index e76a6df1eba1..8fd9eed41fe1 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -3412,3 +3412,55 @@ void xe_vm_snapshot_free(struct xe_vm_snapshot *snap)
 	}
 	kvfree(snap);
 }
+
+int xe_vm_userptr_access(struct xe_userptr_vma *uvma, u64 offset,
+			 void *buf, u64 len, bool write)
+{
+	struct xe_vm *vm = xe_vma_vm(&uvma->vma);
+	struct xe_userptr *up = &uvma->userptr;
+	struct page **page;
+	u64 left = len;
+	int ret = 0;
+
+	while (true) {
+		down_read(&vm->userptr.notifier_lock);
+		if (!xe_vma_userptr_check_repin(uvma))
+			break;
+
+		spin_lock(&vm->userptr.invalidated_lock);
+		list_del_init(&uvma->userptr.invalidate_link);
+		spin_unlock(&vm->userptr.invalidated_lock);
+
+		up_read(&vm->userptr.notifier_lock);
+		ret = xe_vma_userptr_pin_pages(uvma);
+		if (ret)
+			return ret;
+	}
+
+	if (!up->sg) {
+		ret = -EINVAL;
+		goto out_unlock_notifier;
+	}
+
+	page = &up->pages[offset >> PAGE_SHIFT];
+	offset &= ~PAGE_MASK;
+	for (;left > 0; ++page) {
+		u64 cur_len = min(PAGE_SIZE - offset, left);
+		void *ptr = kmap_local_page(page[0]);
+
+		if (write)
+			memcpy(ptr + offset, buf, cur_len);
+		else
+			memcpy(buf, ptr + offset, cur_len);
+		kunmap_local(ptr);
+		buf += cur_len;
+		left -= cur_len;
+		offset = 0;
+	}
+
+	ret = len;
+
+out_unlock_notifier:
+	up_read(&vm->userptr.notifier_lock);
+	return ret;
+}
diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h
index c864dba35e1d..165eab494d59 100644
--- a/drivers/gpu/drm/xe/xe_vm.h
+++ b/drivers/gpu/drm/xe/xe_vm.h
@@ -281,3 +281,6 @@ struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm *vm);
 void xe_vm_snapshot_capture_delayed(struct xe_vm_snapshot *snap);
 void xe_vm_snapshot_print(struct xe_vm_snapshot *snap, struct drm_printer *p);
 void xe_vm_snapshot_free(struct xe_vm_snapshot *snap);
+
+int xe_vm_userptr_access(struct xe_userptr_vma *uvma, u64 offset,
+			 void *buf, u64 len, bool write);
-- 
2.34.1


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

* Re: [PATCH 2/2] drm/xe/eudebug: implement userptr_vma access
  2024-10-28 16:19                 ` [PATCH 2/2] drm/xe/eudebug: implement userptr_vma access Andrzej Hajda
@ 2024-10-28 16:55                   ` Hajda, Andrzej
  0 siblings, 0 replies; 40+ messages in thread
From: Hajda, Andrzej @ 2024-10-28 16:55 UTC (permalink / raw)
  To: Matthew Brost; +Cc: intel-xe, Mika Kuoppala, Jonathan Cavitt

Hi Matt,

W dniu 28.10.2024 o 17:19, Andrzej Hajda pisze:
> Debugger needs to read/write program's vmas including userptr_vma.
> Since hmm_range_fault is used to pin userptr vmas, it is possible
> to map those vmas from debugger context.
>
> v2: pin pages vs notifier, move to vm.c (Matthew)
> v3: - iterate over system pages instead of DMA, fixes work with iommu enabled
>      - s/xe_uvma_access/xe_vm_uvma_access/ (Matt)
> v4: use xe_userptr->pages, instead of sg to access pages (Matt)
>
> Signed-off-by: Andrzej Hajda <andrzej.hajda@intel.com>
> ---

I have dropped hmm_range_fault approach, because it seems too 
complicated for me.

As I understand it requires whole locking dance as described in [1], 
probably also with added timeout handling.


[1]: 
https://www.kernel.org/doc/html/latest/mm/hmm.html#address-space-mirroring-implementation-and-api

Regards

Andrzej


>   drivers/gpu/drm/xe/xe_eudebug.c |  2 +-
>   drivers/gpu/drm/xe/xe_vm.c      | 52 +++++++++++++++++++++++++++++++++
>   drivers/gpu/drm/xe/xe_vm.h      |  3 ++
>   3 files changed, 56 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
> index 1bff0a2cfaa1..569c8d0b2ef8 100644
> --- a/drivers/gpu/drm/xe/xe_eudebug.c
> +++ b/drivers/gpu/drm/xe/xe_eudebug.c
> @@ -3049,7 +3049,7 @@ static int xe_eudebug_vma_access(struct xe_vma *vma, u64 offset,
>   		return ret;
>   	}
>   
> -	return -EINVAL;
> +	return xe_vm_userptr_access(to_userptr_vma(vma), offset, buf, bytes, write);
>   }
>   
>   static int xe_eudebug_vm_access(struct xe_vm *vm, u64 offset,
> diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
> index e76a6df1eba1..8fd9eed41fe1 100644
> --- a/drivers/gpu/drm/xe/xe_vm.c
> +++ b/drivers/gpu/drm/xe/xe_vm.c
> @@ -3412,3 +3412,55 @@ void xe_vm_snapshot_free(struct xe_vm_snapshot *snap)
>   	}
>   	kvfree(snap);
>   }
> +
> +int xe_vm_userptr_access(struct xe_userptr_vma *uvma, u64 offset,
> +			 void *buf, u64 len, bool write)
> +{
> +	struct xe_vm *vm = xe_vma_vm(&uvma->vma);
> +	struct xe_userptr *up = &uvma->userptr;
> +	struct page **page;
> +	u64 left = len;
> +	int ret = 0;
> +
> +	while (true) {
> +		down_read(&vm->userptr.notifier_lock);
> +		if (!xe_vma_userptr_check_repin(uvma))
> +			break;
> +
> +		spin_lock(&vm->userptr.invalidated_lock);
> +		list_del_init(&uvma->userptr.invalidate_link);
> +		spin_unlock(&vm->userptr.invalidated_lock);
> +
> +		up_read(&vm->userptr.notifier_lock);
> +		ret = xe_vma_userptr_pin_pages(uvma);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (!up->sg) {
> +		ret = -EINVAL;
> +		goto out_unlock_notifier;
> +	}
> +
> +	page = &up->pages[offset >> PAGE_SHIFT];
> +	offset &= ~PAGE_MASK;
> +	for (;left > 0; ++page) {
> +		u64 cur_len = min(PAGE_SIZE - offset, left);
> +		void *ptr = kmap_local_page(page[0]);
> +
> +		if (write)
> +			memcpy(ptr + offset, buf, cur_len);
> +		else
> +			memcpy(buf, ptr + offset, cur_len);
> +		kunmap_local(ptr);
> +		buf += cur_len;
> +		left -= cur_len;
> +		offset = 0;
> +	}
> +
> +	ret = len;
> +
> +out_unlock_notifier:
> +	up_read(&vm->userptr.notifier_lock);
> +	return ret;
> +}
> diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h
> index c864dba35e1d..165eab494d59 100644
> --- a/drivers/gpu/drm/xe/xe_vm.h
> +++ b/drivers/gpu/drm/xe/xe_vm.h
> @@ -281,3 +281,6 @@ struct xe_vm_snapshot *xe_vm_snapshot_capture(struct xe_vm *vm);
>   void xe_vm_snapshot_capture_delayed(struct xe_vm_snapshot *snap);
>   void xe_vm_snapshot_print(struct xe_vm_snapshot *snap, struct drm_printer *p);
>   void xe_vm_snapshot_free(struct xe_vm_snapshot *snap);
> +
> +int xe_vm_userptr_access(struct xe_userptr_vma *uvma, u64 offset,
> +			 void *buf, u64 len, bool write);

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

end of thread, other threads:[~2024-10-28 16:55 UTC | newest]

Thread overview: 40+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-01 14:42 [PATCH 00/18] GPU debug support (eudebug) v2 Mika Kuoppala
2024-10-01 14:42 ` [PATCH 01/18] ptrace: export ptrace_may_access Mika Kuoppala
2024-10-01 14:42 ` [PATCH 02/18] drm/xe/eudebug: Introduce eudebug support Mika Kuoppala
2024-10-01 14:42 ` [PATCH 03/18] drm/xe/eudebug: Introduce discovery for resources Mika Kuoppala
2024-10-03  0:41   ` Matthew Brost
2024-10-03  7:27     ` Mika Kuoppala
2024-10-03 16:32       ` Matthew Brost
2024-10-01 14:42 ` [PATCH 04/18] drm/xe/eudebug: Introduce exec_queue events Mika Kuoppala
2024-10-01 14:42 ` [PATCH 05/18] drm/xe/eudebug: hw enablement for eudebug Mika Kuoppala
2024-10-01 14:42 ` [PATCH 06/18] drm/xe: Add EUDEBUG_ENABLE exec queue property Mika Kuoppala
2024-10-01 14:42 ` [PATCH 07/18] drm/xe/eudebug: Introduce per device attention scan worker Mika Kuoppala
2024-10-01 14:42 ` [PATCH 08/18] drm/xe/eudebug: Introduce EU control interface Mika Kuoppala
2024-10-01 14:42 ` [PATCH 09/18] drm/xe/eudebug: Add vm bind and vm bind ops Mika Kuoppala
2024-10-01 14:42 ` [PATCH 10/18] drm/xe/eudebug: Add UFENCE events with acks Mika Kuoppala
2024-10-01 14:42 ` [PATCH 11/18] drm/xe/eudebug: vm open/pread/pwrite Mika Kuoppala
2024-10-01 14:43 ` [PATCH 12/18] drm/xe/eudebug: implement userptr_vma access Mika Kuoppala
2024-10-05  3:48   ` Matthew Brost
2024-10-12  2:39   ` Matthew Brost
2024-10-12  2:55     ` Matthew Brost
2024-10-20 18:16   ` Matthew Brost
2024-10-21  9:54     ` Hajda, Andrzej
2024-10-21 22:34       ` Matthew Brost
2024-10-23 11:32         ` Hajda, Andrzej
2024-10-24 16:06           ` Matthew Brost
2024-10-25 13:20             ` Hajda, Andrzej
2024-10-25 18:23               ` Matthew Brost
2024-10-28 16:19                 ` [PATCH 1/2] drm/xe: keep list of system pages in xe_userptr Andrzej Hajda
2024-10-28 16:19                 ` [PATCH 2/2] drm/xe/eudebug: implement userptr_vma access Andrzej Hajda
2024-10-28 16:55                   ` Hajda, Andrzej
2024-10-01 14:43 ` [PATCH 13/18] drm/xe: Debug metadata create/destroy ioctls Mika Kuoppala
2024-10-01 16:25   ` Matthew Auld
2024-10-02 13:59     ` Grzegorzek, Dominik
2024-10-01 14:43 ` [PATCH 14/18] drm/xe: Attach debug metadata to vma Mika Kuoppala
2024-10-01 14:43 ` [PATCH 15/18] drm/xe/eudebug: Add debug metadata support for xe_eudebug Mika Kuoppala
2024-10-01 14:43 ` [PATCH 16/18] drm/xe/eudebug: Implement vm_bind_op discovery Mika Kuoppala
2024-10-01 14:43 ` [PATCH 17/18] drm/xe/eudebug: Dynamically toggle debugger functionality Mika Kuoppala
2024-10-01 14:43 ` [PATCH 18/18] drm/xe/eudebug_test: Introduce xe_eudebug wa kunit test Mika Kuoppala
2024-10-01 15:02 ` ✓ CI.Patch_applied: success for GPU debug support (eudebug) (rev2) Patchwork
2024-10-01 15:03 ` ✗ CI.checkpatch: warning " Patchwork
2024-10-01 15:03 ` ✗ CI.KUnit: failure " Patchwork

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