public inbox for igt-dev@lists.freedesktop.org
 help / color / mirror / Atom feed
* [RFC PATCH i-g-t 0/2] Xe driver watch-queue implementation and example
@ 2026-03-04 12:17 Thomas Hellström
  2026-03-04 12:17 ` [RFC PATCH i-g-t 1/2] lib/xe: add xe_watch listener for watch queue events Thomas Hellström
  2026-03-04 12:17 ` [RFC PATCH i-g-t 2/2] tests/intel/xe_exec_compute_mode: Add a listener for file events Thomas Hellström
  0 siblings, 2 replies; 3+ messages in thread
From: Thomas Hellström @ 2026-03-04 12:17 UTC (permalink / raw)
  To: igt-dev
  Cc: Thomas Hellström, Matthew Brost, Maarten Lankhorst,
	Michal Mrozek, John Falkowski, Rodrigo Vivi, Lahtinen Joonas

This series aims to provide an example how to use the xe driver watch-queue
interface. The interface provides a means for user-space render clients to get
asynchronous notifications with metadata attached.

The primary use-case would be the LR mode rebind worker running out of
memory. Currently the VM gets killed, but it should be possible to
recover, unbind buffer objects and restart.

Thomas Hellström (2):
  lib/xe: add xe_watch listener for watch queue events
  tests/intel/xe_exec_compute_mode: Add a listener for file events

 include/drm-uapi/xe_drm.h          |  46 ++++++
 include/drm-uapi/xe_drm_events.h   |  65 +++++++++
 lib/meson.build                    |   1 +
 lib/xe/xe_watch.c                  | 221 +++++++++++++++++++++++++++++
 lib/xe/xe_watch.h                  |  80 +++++++++++
 tests/intel/xe_exec_compute_mode.c |  59 +++++++-
 6 files changed, 470 insertions(+), 2 deletions(-)
 create mode 100644 include/drm-uapi/xe_drm_events.h
 create mode 100644 lib/xe/xe_watch.c
 create mode 100644 lib/xe/xe_watch.h

-- 
2.53.0


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

* [RFC PATCH i-g-t 1/2] lib/xe: add xe_watch listener for watch queue events
  2026-03-04 12:17 [RFC PATCH i-g-t 0/2] Xe driver watch-queue implementation and example Thomas Hellström
@ 2026-03-04 12:17 ` Thomas Hellström
  2026-03-04 12:17 ` [RFC PATCH i-g-t 2/2] tests/intel/xe_exec_compute_mode: Add a listener for file events Thomas Hellström
  1 sibling, 0 replies; 3+ messages in thread
From: Thomas Hellström @ 2026-03-04 12:17 UTC (permalink / raw)
  To: igt-dev
  Cc: Thomas Hellström, Matthew Brost, Maarten Lankhorst,
	Michal Mrozek, John Falkowski, Rodrigo Vivi, Lahtinen Joonas

Add a new xe_watch library module that subscribes an Xe device file
descriptor to kernel watch queue notifications and dispatches them to a
caller-supplied handler from a background pthread.

The public API consists of two functions and three types:

  xe_watch_listener_create(xe_fd, event)
    Opens an O_NOTIFICATION_PIPE, sizes it, filters it to
    WATCH_TYPE_DRM_NOTIFY events, registers it with the Xe device via
    DRM_IOCTL_XE_WATCH_QUEUE, and starts a worker pthread.

  xe_watch_listener_destroy(listener)
    Signals the worker to exit via an internal stop-pipe, joins the
    thread, and frees all resources.

  struct xe_watch_event / xe_watch_event_ops / xe_watch_event_fn
    Caller embeds struct xe_watch_event in its own private state and
    points ops->event_deliver at its handler. The worker sets
    event->notif (a struct watch_notification *) before each call, and
    the handler uses container_of() to recover its private context.

The implementation relies on the userspace-safe guard added to
<linux/watch_queue.h> so it can be included from xe_drm.h directly.
A WATCH_TYPE_DRM_XE_NOTIFY fallback define is provided for build
environments whose system header predates that define.

xe_drm.h is updated to match the upstream watch_queue branch:
  - Add DRM_XE_WATCH_QUEUE ioctl number (0x0f) and
    DRM_IOCTL_XE_WATCH_QUEUE macro.
  - Add struct drm_xe_watch_queue, enum drm_xe_watch_event
    (DRM_XE_WATCH_EVENT_VM_ERR = 0), and
    struct drm_xe_watch_notification_vm_oom.

Assisted-by: GitHub Copilot:claude-sonnet-4.6
Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
---
 include/drm-uapi/xe_drm.h        |  46 +++++++
 include/drm-uapi/xe_drm_events.h |  65 +++++++++
 lib/meson.build                  |   1 +
 lib/xe/xe_watch.c                | 221 +++++++++++++++++++++++++++++++
 lib/xe/xe_watch.h                |  80 +++++++++++
 5 files changed, 413 insertions(+)
 create mode 100644 include/drm-uapi/xe_drm_events.h
 create mode 100644 lib/xe/xe_watch.c
 create mode 100644 lib/xe/xe_watch.h

diff --git a/include/drm-uapi/xe_drm.h b/include/drm-uapi/xe_drm.h
index f7abd9b37..934d92634 100644
--- a/include/drm-uapi/xe_drm.h
+++ b/include/drm-uapi/xe_drm.h
@@ -83,6 +83,7 @@ extern "C" {
  *  - &DRM_IOCTL_XE_OBSERVATION
  *  - &DRM_IOCTL_XE_MADVISE
  *  - &DRM_IOCTL_XE_VM_QUERY_MEM_RANGE_ATTRS
+ *  - &DRM_IOCTL_XE_WATCH_QUEUE
  */
 
 /*
@@ -107,6 +108,7 @@ extern "C" {
 #define DRM_XE_MADVISE			0x0c
 #define DRM_XE_VM_QUERY_MEM_RANGE_ATTRS	0x0d
 #define DRM_XE_EXEC_QUEUE_SET_PROPERTY	0x0e
+#define DRM_XE_WATCH_QUEUE		0x0f
 
 /* Must be kept compact -- no holes */
 
@@ -125,6 +127,7 @@ extern "C" {
 #define DRM_IOCTL_XE_MADVISE			DRM_IOW(DRM_COMMAND_BASE + DRM_XE_MADVISE, struct drm_xe_madvise)
 #define DRM_IOCTL_XE_VM_QUERY_MEM_RANGE_ATTRS	DRM_IOWR(DRM_COMMAND_BASE + DRM_XE_VM_QUERY_MEM_RANGE_ATTRS, struct drm_xe_vm_query_mem_range_attr)
 #define DRM_IOCTL_XE_EXEC_QUEUE_SET_PROPERTY	DRM_IOW(DRM_COMMAND_BASE + DRM_XE_EXEC_QUEUE_SET_PROPERTY, struct drm_xe_exec_queue_set_property)
+#define DRM_IOCTL_XE_WATCH_QUEUE		DRM_IOW(DRM_COMMAND_BASE + DRM_XE_WATCH_QUEUE, struct drm_xe_watch_queue)
 
 /**
  * DOC: Xe IOCTL Extensions
@@ -2363,6 +2366,49 @@ struct drm_xe_exec_queue_set_property {
 	__u64 reserved[2];
 };
 
+/**
+ * DOC: DRM_XE_WATCH_QUEUE
+ *
+ * Subscribe a notification pipe to receive device events for the calling
+ * process's DRM file handle.  Events are scoped to the subscribing file:
+ * only events that belong to that file (for example, VM error on a VM created
+ * through the same file) are delivered, preventing information leaks between
+ * processes sharing the same GPU device.
+ *
+ * The pipe must first be opened with O_NOTIFICATION_PIPE (i.e. O_EXCL passed
+ * to pipe2()) and sized via %IOC_WATCH_QUEUE_SET_SIZE before subscribing.
+ *
+ * Events are delivered as notification records read from the pipe.  The
+ * @watch_id field is embedded in the notification info field and can be used
+ * to distinguish multiple watches sharing a pipe.
+ *
+ * Currently defined event subtypes:
+ *  - %DRM_XE_WATCH_EVENT_VM_ERR - a VM owned by this file has encountered an error
+ */
+
+/**
+ * struct drm_xe_watch_queue - subscribe to device event notifications
+ *
+ * Used with %DRM_IOCTL_XE_WATCH_QUEUE.  Notifications are scoped to the
+ * DRM file handle used to issue this IOCTL.
+ */
+struct drm_xe_watch_queue {
+	/** @fd: file descriptor of pipe opened with O_NOTIFICATION_PIPE */
+	__u32 fd;
+
+	/**
+	 * @watch_id: identifier (0–255) embedded in the watch notification
+	 * info field; allows multiplexing several watches on one pipe
+	 */
+	__u32 watch_id;
+
+	/** @flags: must be zero */
+	__u32 flags;
+
+	/** @pad: reserved, must be zero */
+	__u32 pad;
+};
+
 #if defined(__cplusplus)
 }
 #endif
diff --git a/include/drm-uapi/xe_drm_events.h b/include/drm-uapi/xe_drm_events.h
new file mode 100644
index 000000000..604955950
--- /dev/null
+++ b/include/drm-uapi/xe_drm_events.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#ifndef _UAPI_XE_DRM_EVENTS_H_
+#define _UAPI_XE_DRM_EVENTS_H_
+
+#include <linux/types.h>
+#include <linux/watch_queue.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/*
+ * WATCH_TYPE_DRM_XE_NOTIFY is defined in the xe watch_queue branch; provide
+ * a fallback for system headers that do not yet carry it.
+ */
+#ifndef WATCH_TYPE_DRM_XE_NOTIFY
+#define WATCH_TYPE_DRM_XE_NOTIFY 2
+#endif
+
+/**
+ * enum drm_xe_watch_event - Xe device watch event subtypes
+ *
+ * Subtypes for notifications delivered via %WATCH_TYPE_DRM_XE_NOTIFY when
+ * reading from a pipe subscribed with %DRM_IOCTL_XE_WATCH_QUEUE.
+ */
+enum drm_xe_watch_event {
+	/**
+	 * @DRM_XE_WATCH_EVENT_VM_ERR: a VM has encountered an error.
+	 *
+	 * Indicates that a memory allocation failure occurred within the
+	 * given VM.  The vm_id of the affected VM is carried in the
+	 * @drm_xe_watch_notification_vm_err::vm_id field of the extended
+	 * notification record.
+	 */
+	DRM_XE_WATCH_EVENT_VM_ERR = 0,
+};
+
+/**
+ * struct drm_xe_watch_notification_vm_err - VM error event notification
+ *
+ * Notification record delivered for %DRM_XE_WATCH_EVENT_VM_ERR.
+ * The record type is always %WATCH_TYPE_DRM_XE_NOTIFY and the subtype is
+ * %DRM_XE_WATCH_EVENT_VM_ERR.
+ */
+struct drm_xe_watch_notification_vm_err {
+	/** @base: common watch notification header */
+	struct watch_notification base;
+
+	/** @vm_id: ID of the VM that hit out-of-memory */
+	__u32 vm_id;
+
+	/** @error_code: error code describing the error condition (negative errno) */
+	__s32 error_code;
+};
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* _UAPI_XE_DRM_H_ */
diff --git a/lib/meson.build b/lib/meson.build
index ea721ecf7..1fa2cac0b 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -132,6 +132,7 @@ lib_sources = [
 	'xe/xe_sriov_debugfs.c',
 	'xe/xe_sriov_provisioning.c',
 	'xe/xe_util.c',
+	'xe/xe_watch.c',
 ]
 
 lib_deps = [
diff --git a/lib/xe/xe_watch.c b/lib/xe/xe_watch.c
new file mode 100644
index 000000000..46febb18a
--- /dev/null
+++ b/lib/xe/xe_watch.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#include <errno.h>
+#include <poll.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include <xf86drm.h>
+
+#include "igt_core.h"
+#include "xe_drm.h"
+#include "xe_drm_events.h"
+#include "xe/xe_watch.h"
+
+/* Pipe ring-buffer size in pages (one page holds multiple records). */
+#define XE_WATCH_PIPE_PAGES	1
+
+/* Read buffer – large enough for any single notification record. */
+#define XE_WATCH_BUF_SIZE	512
+
+/**
+ * struct xe_watch_listener - internal state for an Xe watch queue listener
+ * @xe_fd: xe DRM device file descriptor passed to xe_watch_listener_create()
+ * @pipe_fd: read end of the O_NOTIFICATION_PIPE used to receive kernel events
+ * @stop_pipe: internal pipe pair used to unblock the worker; write end is
+ *             written by xe_watch_listener_destroy() to signal the thread to exit
+ * @event: caller-supplied event object dispatched for each notification
+ * @thread: background pthread running xe_watch_worker()
+ * @running: set to %false by xe_watch_listener_destroy() to request exit
+ */
+struct xe_watch_listener {
+	int xe_fd;
+	int pipe_fd;
+	int stop_pipe[2];
+	struct xe_watch_event *event;
+	pthread_t thread;
+	bool running;
+};
+
+static void *xe_watch_worker(void *arg)
+{
+	struct xe_watch_listener *l = arg;
+	unsigned char buf[XE_WATCH_BUF_SIZE];
+	struct pollfd pfds[2] = {
+		{ .fd = l->pipe_fd,      .events = POLLIN },
+		{ .fd = l->stop_pipe[0], .events = POLLIN },
+	};
+
+	while (READ_ONCE(l->running)) {
+		unsigned char *p, *end;
+		ssize_t len;
+		int ret;
+
+		ret = poll(pfds, 2, 200/* ms */);
+		if (ret < 0) {
+			if (errno == EINTR)
+				continue;
+			igt_warn("xe_watch: poll failed: %s\n", strerror(errno));
+			break;
+		}
+
+		/* Stop signal received */
+		if (pfds[1].revents & POLLIN)
+			break;
+
+		if (!(pfds[0].revents & POLLIN))
+			continue;
+
+		len = read(l->pipe_fd, buf, sizeof(buf));
+
+		if (len < 0) {
+			if (errno == EINTR || errno == EAGAIN)
+				continue;
+			igt_warn("xe_watch: read failed: %s\n", strerror(errno));
+			break;
+		}
+
+		p = buf;
+		end = buf + len;
+
+		while (p + sizeof(struct watch_notification) <= end) {
+			const struct watch_notification *notif =
+				(const struct watch_notification *)p;
+			size_t rec_len = notif->info & WATCH_INFO_LENGTH;
+
+			if (rec_len < sizeof(struct watch_notification) ||
+			    p + rec_len > end)
+				break;
+
+			l->event->notif = notif;
+			(*l->event->ops->event_deliver)(l->event);
+
+			p += rec_len;
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * xe_watch_listener_create - create and start an Xe watch queue listener
+ * @xe_fd: open xe DRM device file descriptor
+ * @event: caller-initialised event object; its @ops->event_deliver function
+ *         is called from a background pthread for every received
+ *         %WATCH_TYPE_DRM_XE_NOTIFY record, with @event->notif pointing to the
+ *         notification.  The caller typically embeds @event in a larger
+ *         private structure and uses container_of() inside the callback.
+ *
+ * Opens an ``O_NOTIFICATION_PIPE``, registers it with the Xe device via
+ * %DRM_IOCTL_XE_WATCH_QUEUE, and starts a background pthread that reads
+ * events and dispatches them via @event->ops->event_deliver().
+ *
+ * Returns a pointer to the new listener on success, or asserts on failure.
+ * Free with xe_watch_listener_destroy().
+ */
+struct xe_watch_listener *xe_watch_listener_create(int xe_fd,
+						   struct xe_watch_event *event)
+{
+	struct xe_watch_listener *l;
+	struct {
+		struct watch_notification_filter filter;
+		struct watch_notification_type_filter slot[2];
+	} fbuf = {
+		.filter = { .nr_filters = 2 },
+		.slot = {
+			{
+				.type           = WATCH_TYPE_DRM_XE_NOTIFY,
+				.subtype_filter = { [0] = UINT32_MAX },
+			},
+			{
+				.type           = WATCH_TYPE_META,
+				.subtype_filter = { [0] = UINT32_MAX },
+			},
+		},
+	};
+	struct drm_xe_watch_queue args = {};
+	int pipefd[2];
+	int ret;
+
+	igt_assert(event && event->ops && event->ops->event_deliver);
+
+	l = calloc(1, sizeof(*l));
+	igt_assert(l);
+
+	l->xe_fd = xe_fd;
+	l->event = event;
+	WRITE_ONCE(l->running, true);
+
+	/* Create the notification pipe */
+	ret = pipe2(pipefd, O_NOTIFICATION_PIPE);
+	igt_assert_f(ret == 0, "xe_watch: pipe2(O_NOTIFICATION_PIPE) failed: %s\n",
+		     strerror(errno));
+	l->pipe_fd = pipefd[0];
+
+	/* Size the pipe ring buffer */
+	ret = ioctl(l->pipe_fd, IOC_WATCH_QUEUE_SET_SIZE, XE_WATCH_PIPE_PAGES);
+	igt_assert_f(ret == 0, "xe_watch: IOC_WATCH_QUEUE_SET_SIZE failed: %s\n",
+		     strerror(errno));
+
+	/* Filter to only DRM_XE and META notifications */
+	ret = ioctl(l->pipe_fd, IOC_WATCH_QUEUE_SET_FILTER, &fbuf.filter);
+	igt_assert_f(ret == 0, "xe_watch: IOC_WATCH_QUEUE_SET_FILTER failed: %s\n",
+		     strerror(errno));
+
+	/* Register the pipe with the Xe device */
+	args.fd       = pipefd[1];
+	args.watch_id = 0;
+	ret = drmIoctl(xe_fd, DRM_IOCTL_XE_WATCH_QUEUE, &args);
+	igt_assert_f(ret == 0, "xe_watch: DRM_IOCTL_XE_WATCH_QUEUE failed: %s\n",
+		     strerror(errno));
+
+	/*
+	 * The write end is owned by the kernel after subscription; close our
+	 * copy so that the pipe is torn down when the device closes it.
+	 */
+	close(pipefd[1]);
+
+	/* Internal stop-signal pipe */
+	ret = pipe(l->stop_pipe);
+	igt_assert_f(ret == 0, "xe_watch: pipe(stop) failed: %s\n",
+		     strerror(errno));
+
+	ret = pthread_create(&l->thread, NULL, xe_watch_worker, l);
+	igt_assert_f(ret == 0, "xe_watch: pthread_create failed: %s\n",
+		     strerror(ret));
+
+	return l;
+}
+
+/**
+ * xe_watch_listener_destroy - stop and free an Xe watch queue listener
+ * @listener: listener returned by xe_watch_listener_create()
+ *
+ * Signals the worker thread to exit, waits for it to finish, closes file
+ * descriptors and frees the listener.
+ */
+void xe_watch_listener_destroy(struct xe_watch_listener *listener)
+{
+	if (!listener)
+		return;
+
+	/* Signal the worker to exit */
+	WRITE_ONCE(listener->running, false);
+	write(listener->stop_pipe[1], "\0", 1);
+
+	pthread_join(listener->thread, NULL);
+
+	close(listener->stop_pipe[0]);
+	close(listener->stop_pipe[1]);
+	close(listener->pipe_fd);
+
+	free(listener);
+}
diff --git a/lib/xe/xe_watch.h b/lib/xe/xe_watch.h
new file mode 100644
index 000000000..135cfe941
--- /dev/null
+++ b/lib/xe/xe_watch.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#ifndef _XE_WATCH_H_
+#define _XE_WATCH_H_
+
+#include <stdint.h>
+
+#include "xe_drm.h"
+
+struct xe_watch_event;
+
+/**
+ * typedef xe_watch_event_fn - callback invoked for each watch queue event
+ * @event: the event object whose @ops->event_deliver was registered; the
+ *         @notif field is set to the received notification before the call.
+ *         The caller typically uses container_of() to recover its private
+ *         state from @event.
+ */
+typedef void (*xe_watch_event_fn)(struct xe_watch_event *event);
+
+/**
+ * struct xe_watch_event_ops - operations table for &struct xe_watch_event
+ * @event_deliver: called by the watch listener worker for every received
+ *                 %WATCH_TYPE_DRM_XE_NOTIFY record.  Must not be NULL.
+ */
+struct xe_watch_event_ops {
+	xe_watch_event_fn event_deliver;
+};
+
+/**
+ * struct xe_watch_event - base object embedded in caller-private event state
+ * @ops: pointer to the operations table; must be initialised before calling
+ *       xe_watch_listener_create()
+ * @notif: set by the worker to the current notification record immediately
+ *         before dispatching @ops->event_deliver(); valid only for the
+ *         duration of that call.  Cast to
+ *         &struct drm_xe_watch_notification_vm_err when
+ *         @notif->subtype == %DRM_XE_WATCH_EVENT_VM_ERR.
+ *
+ * Callers embed this struct in their own private structure and recover it
+ * inside the callback using container_of():
+ *
+ * .. code-block:: c
+ *
+ *   struct my_state {
+ *       struct xe_watch_event base;
+ *       int my_field;
+ *   };
+ *
+ *   static void on_event(struct xe_watch_event *e)
+ *   {
+ *       struct my_state *s = container_of(e, struct my_state, base);
+ *       // use s->base.notif and s->my_field
+ *   }
+ *
+ *   static const struct xe_watch_event_ops my_ops = {
+ *       .event_deliver = on_event,
+ *   };
+ */
+struct xe_watch_event {
+	const struct xe_watch_event_ops *ops;
+	const struct watch_notification *notif;
+};
+
+/**
+ * struct xe_watch_listener - listener state for Xe device watch queue events
+ *
+ * Opaque handle returned by xe_watch_listener_create().  Do not access
+ * members directly; use the xe_watch_listener_* API.
+ */
+struct xe_watch_listener;
+
+struct xe_watch_listener *xe_watch_listener_create(int xe_fd,
+						   struct xe_watch_event *event);
+void xe_watch_listener_destroy(struct xe_watch_listener *listener);
+
+#endif /* _XE_WATCH_H_ */
-- 
2.53.0


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

* [RFC PATCH i-g-t 2/2] tests/intel/xe_exec_compute_mode: Add a listener for file events
  2026-03-04 12:17 [RFC PATCH i-g-t 0/2] Xe driver watch-queue implementation and example Thomas Hellström
  2026-03-04 12:17 ` [RFC PATCH i-g-t 1/2] lib/xe: add xe_watch listener for watch queue events Thomas Hellström
@ 2026-03-04 12:17 ` Thomas Hellström
  1 sibling, 0 replies; 3+ messages in thread
From: Thomas Hellström @ 2026-03-04 12:17 UTC (permalink / raw)
  To: igt-dev
  Cc: Thomas Hellström, Matthew Brost, Maarten Lankhorst,
	Michal Mrozek, John Falkowski, Rodrigo Vivi, Lahtinen Joonas

Using the new xe_watch library functionality, add a listener
for file events. The listener gets notified on

* File close,
* Lost event,
* Rebind worker error.

But doesn't take any action.

Assisted-by: GitHub Copilot:claude-sonnet-4.6
Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
---
 tests/intel/xe_exec_compute_mode.c | 59 +++++++++++++++++++++++++++++-
 1 file changed, 57 insertions(+), 2 deletions(-)

diff --git a/tests/intel/xe_exec_compute_mode.c b/tests/intel/xe_exec_compute_mode.c
index 438ea163f..0784f3a31 100644
--- a/tests/intel/xe_exec_compute_mode.c
+++ b/tests/intel/xe_exec_compute_mode.c
@@ -22,6 +22,7 @@
 #include "xe/xe_ioctl.h"
 #include "xe/xe_query.h"
 #include "xe/xe_spin.h"
+#include "xe/xe_watch.h"
 #include <string.h>
 
 #define MAX_N_EXECQUEUES 	16
@@ -35,6 +36,52 @@
 #define FREE_MAPPPING			(0x1 << 7)
 #define UNMAP_MAPPPING			(0x1 << 8)
 
+static void xe_event_fn(struct xe_watch_event *event)
+{
+	const struct watch_notification *notif = event->notif;
+	const struct drm_xe_watch_notification_vm_err *err_event =
+		igt_container_of(notif, err_event, base);
+
+	switch (notif->type) {
+	case WATCH_TYPE_META:
+		switch (notif->subtype) {
+		case WATCH_META_REMOVAL_NOTIFICATION:
+			igt_info("The device file was closed.\n");
+			break;
+		case WATCH_META_LOSS_NOTIFICATION:
+			igt_warn("The listener lost a message.\n");
+			break;
+		default:
+			igt_warn("Unknown META subtype.\n");
+			break;
+		}
+		break;
+
+	case WATCH_TYPE_DRM_XE_NOTIFY:
+		switch (notif->subtype) {
+		case DRM_XE_WATCH_EVENT_VM_ERR:
+			igt_info("VM with id %u saw an error: %d\n",
+				 (unsigned int) err_event->vm_id,
+				 (int) err_event->error_code);
+			break;
+		default:
+			igt_warn("Unknown XE watch subtype %u\n",
+				 (unsigned int) notif->subtype);
+			break;
+		}
+
+		break;
+
+	default:
+		igt_warn("Unknown watch type %u.\n", notif->type);
+		break;
+	}
+}
+
+const struct xe_watch_event_ops event_ops = {
+	.event_deliver = xe_event_fn,
+};
+
 /**
  * SUBTEST: twice-%s
  * Description: Run %arg[1] compute machine test twice
@@ -430,9 +477,15 @@ int igt_main()
 		{ NULL },
 	};
 	int fd;
+	struct xe_watch_event watch_event = {
+		.ops = &event_ops,
+	};
+	struct xe_watch_listener *listener;
 
-	igt_fixture()
+	igt_fixture() {
 		fd = drm_open_driver(DRIVER_XE);
+		listener = xe_watch_listener_create(fd, &watch_event);
+	}
 
 	for (const struct section *s = sections; s->name; s++) {
 		igt_subtest_f("once-%s", s->name)
@@ -465,6 +518,8 @@ int igt_main()
 		lr_mode_workload(fd);
 
 
-	igt_fixture()
+	igt_fixture() {
 		drm_close_driver(fd);
+		xe_watch_listener_destroy(listener);
+	}
 }
-- 
2.53.0


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

end of thread, other threads:[~2026-03-04 12:17 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-04 12:17 [RFC PATCH i-g-t 0/2] Xe driver watch-queue implementation and example Thomas Hellström
2026-03-04 12:17 ` [RFC PATCH i-g-t 1/2] lib/xe: add xe_watch listener for watch queue events Thomas Hellström
2026-03-04 12:17 ` [RFC PATCH i-g-t 2/2] tests/intel/xe_exec_compute_mode: Add a listener for file events Thomas Hellström

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