From nobody Thu Jun 25 05:23:11 2026 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A078B35E1B0 for ; Fri, 12 Jun 2026 13:54:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781272460; cv=none; b=mH3kUdYVGW+ylOsCoDBE7vdt/xFxyycvvmKuuGP5JwPEU3gOA53HvptOe8cNYBuP0lGSntxAFnKdCVmtmQLSqd3kqIUqwWMRPixjUaLfC6BYhxrw9EQTsnqYYJr/V26K4WeEuM6F2+5zkAQEyXdqHssiQz6BRZFoxL2ym7FAdfg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781272460; c=relaxed/simple; bh=Xd4vQf1jFgQ6QbLvRZ+VlWHLkNR/Dv9QvYXejrJN10E=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Pnod1/TO1rrHXf74IKAHn6c9RNF6785z0cWFeU84rD5SHNovEOu9rD9UkS+0IQg4WqdFvgn88f/dmc0Y6gdGgwCPDFGkfl25/ZOo7TrVjsEbninXhS8D/wR9GbLQ4vzTKeM70T0TpYPpqiLB1pzr60bnUHHgY51VPGfHv5VNd/c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com; spf=pass smtp.mailfrom=linux.intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=bvj8IGOr; arc=none smtp.client-ip=198.175.65.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="bvj8IGOr" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1781272459; x=1812808459; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=Xd4vQf1jFgQ6QbLvRZ+VlWHLkNR/Dv9QvYXejrJN10E=; b=bvj8IGOrskfQn6z5+5Ky7dYZ7rAlFgS290HJQKBb9KMTuFdk3NpPvGFG CFjXoGN5W+RWtS7Zs+TNd9hEthSyiwUw3Ep630QyqQuCpGjP8TvsZulz9 i5jgdloE76rrfaJnjztP9tDPC2CHb7epXHZDEi4zkoz7/N5JfTJ6GnpIy PVxTgx96a9DmjZ9KwSZ9jefN88xBpBFLm4+SimvErrA/pUiPLuWXSxCYK PgyfTzIEcNHDwt63aulEFvUAelzGMhn6MXT5wnI2xe8cH/XEBiArCL6q4 QC3/PqqMrmupMeHOtiap+oVDYbsvaBSULQa+i924jqcXbmtdGm1Uvg02f Q==; X-CSE-ConnectionGUID: UbvfTJoTSCiIAjW/1mCq1Q== X-CSE-MsgGUID: kylL0BVARTuJdu07wQJQPQ== X-IronPort-AV: E=McAfee;i="6800,10657,11813"; a="82163301" X-IronPort-AV: E=Sophos;i="6.24,200,1774335600"; d="scan'208";a="82163301" Received: from fmviesa006.fm.intel.com ([10.60.135.146]) by orvoesa110.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 12 Jun 2026 06:54:19 -0700 X-CSE-ConnectionGUID: a8PQE+BcRiKR39lSG/Y8Ag== X-CSE-MsgGUID: 9R1Ke0IXShGdMTPaRUArlg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.24,200,1774335600"; d="scan'208";a="242446533" Received: from slindbla-desk.ger.corp.intel.com (HELO fedora) ([10.245.245.68]) by fmviesa006-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 12 Jun 2026 06:54:14 -0700 From: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= To: intel-xe@lists.freedesktop.org Cc: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= , Matthew Brost , Maarten Lankhorst , Michal Mrozek , John Falkowski , Rodrigo Vivi , Lahtinen Joonas , David Howells , Christian Brauner , Kees Cook , Davidlohr Bueso , =?UTF-8?q?Christian=20K=C3=B6nig?= , Dave Airlie , Simona Vetter , dri-devel@lists.freedesktop.org, LMKL Subject: [PATCH 1/4] drm/xe: Add DRM_IOCTL_XE_VM_RESTART IOCTL Date: Fri, 12 Jun 2026 15:53:37 +0200 Message-ID: <20260612135340.116100-2-thomas.hellstrom@linux.intel.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260612135340.116100-1-thomas.hellstrom@linux.intel.com> References: <20260612135340.116100-1-thomas.hellstrom@linux.intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Add an async VM restart IOCTL that allows userspace to re-queue the preempt-rebind worker for a VM that has been paused after a recoverable error. Add xe_vm_restart_ioctl() which: - Looks up the VM by id via xe_vm_lookup() - Returns -EINVAL if the VM is not in preempt-fence mode or not restartable - Returns -EALREADY if the VM is not currently paused - Queues the rebind worker via and returns 0 If the optional @timestamp_ns field is non-zero, logs the latency between that timestamp and the point the worker is queued. Add DRM_XE_VM_CREATE_FLAG_RESTARTABLE to opt a VM in to the restartable behaviour: on recoverable errors (-ENOMEM, -ENOSPC) the rebind worker is deactivated rather than the VM being killed. Requires DRM_XE_VM_CREATE_FLAG_LR_MODE and may not be used with DRM_XE_VM_CREATE_FLAG_FAULT_MODE. Add struct drm_xe_vm_restart UAPI struct with vm_id, pad, timestamp_ns and reserved fields, and register the IOCTL at slot 0x10. Assisted-by: GitHub_Copilot:claude-sonnet-4.6 Signed-off-by: Thomas Hellstr=C3=B6m diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index 51e3a2dd7b22..867d7c55dc03 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -215,6 +215,7 @@ static const struct drm_ioctl_desc xe_ioctls[] =3D { DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(XE_VM_GET_PROPERTY, xe_vm_get_property_ioctl, DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(XE_VM_RESTART, xe_vm_restart_ioctl, DRM_RENDER_ALLOW), }; static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned lon= g arg) diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index 75841f3e9afa..86ed8f31a219 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -563,8 +563,14 @@ static void preempt_rebind_work_func(struct work_struc= t *w) } if (err) { - drm_warn(&vm->xe->drm, "VM worker error: %d\n", err); - xe_vm_kill(vm, true); + if ((err =3D=3D -ENOMEM || err =3D=3D -ENOSPC) && xe_vm_is_restartable(v= m)) { + vm->preempt.rebind_deactivated =3D true; + drm_dbg(&vm->xe->drm, "Rebind deactivated VM on error %pe\n", + ERR_PTR(err)); + } else { + drm_warn(&vm->xe->drm, "VM worker error: %d\n", err); + xe_vm_kill(vm, true); + } } up_write(&vm->lock); @@ -573,6 +579,85 @@ static void preempt_rebind_work_func(struct work_struc= t *w) trace_xe_vm_rebind_worker_exit(vm); } +/** + * xe_vm_restart_ioctl() - Queue the preempt-rebind worker for a paused VM + * @dev: DRM device + * @data: pointer to &struct drm_xe_vm_restart from userspace + * @file: DRM file handle + * + * Looks up the VM identified by @vm_id and, if it is currently paused (its + * rebind worker was deactivated after a recoverable error), clears the pa= used + * state and queues the rebind worker. Only valid for VMs in preempt-fence + * mode. + * + * If @timestamp_ns is non-zero, logs the latency between that timestamp a= nd + * the point the vm lock is taken, regardless of whether the VM was paused. + * + * Return: 0 if the worker was queued, -EALREADY if the VM is not paused, + * -EINVAL if the VM is not in preempt-fence mode or not restartab= le, + * -ENOENT if the VM was not found. + */ +int xe_vm_restart_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct xe_device *xe =3D to_xe_device(dev); + struct xe_file *xef =3D to_xe_file(file); + struct drm_xe_vm_restart *args =3D data; + struct xe_vm *vm; + int err =3D 0; + + if (XE_IOCTL_DBG(xe, args->reserved || args->pad)) + return -EINVAL; + + vm =3D xe_vm_lookup(xef, args->vm_id); + if (XE_IOCTL_DBG(xe, !vm)) + return -ENOENT; + + if (XE_IOCTL_DBG(xe, !xe_vm_in_preempt_fence_mode(vm))) { + xe_vm_put(vm); + return -EINVAL; + } + + if (XE_IOCTL_DBG(xe, !xe_vm_is_restartable(vm))) { + xe_vm_put(vm); + return -EINVAL; + } + + err =3D down_read_interruptible(&vm->lock); + if (err) + goto out; + + if (XE_IOCTL_DBG(xe, xe_vm_is_closed_or_banned(vm))) { + err =3D -ENOENT; + goto out_unlock_read; + } + + if (args->timestamp_ns) { + u64 delay_us =3D (ktime_get_ns() - args->timestamp_ns) / NSEC_PER_USEC; + + drm_dbg(&xe->drm, "VM %u restart latency: %llu us\n", + args->vm_id, delay_us); + } + + err =3D xe_vm_lock(vm, true); + if (err) + goto out_unlock_read; + + if (!vm->preempt.rebind_deactivated) { + err =3D -EALREADY; + goto out_unlock_resv; + } + + xe_vm_reactivate_rebind(vm); +out_unlock_resv: + xe_vm_unlock(vm); +out_unlock_read: + up_read(&vm->lock); +out: + xe_vm_put(vm); + return err; +} + /** * xe_vm_add_fault_entry_pf() - Add pagefault to vm fault list * @vm: The VM. @@ -2049,7 +2134,8 @@ find_ufence_get(struct xe_sync_entry *syncs, u32 num_= syncs) #define ALL_DRM_XE_VM_CREATE_FLAGS (DRM_XE_VM_CREATE_FLAG_SCRATCH_PAGE | \ DRM_XE_VM_CREATE_FLAG_LR_MODE | \ DRM_XE_VM_CREATE_FLAG_FAULT_MODE | \ - DRM_XE_VM_CREATE_FLAG_NO_VM_OVERCOMMIT) + DRM_XE_VM_CREATE_FLAG_NO_VM_OVERCOMMIT | \ + DRM_XE_VM_CREATE_FLAG_RESTARTABLE) int xe_vm_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file) @@ -2092,6 +2178,11 @@ int xe_vm_create_ioctl(struct drm_device *dev, void = *data, args->flags & DRM_XE_VM_CREATE_FLAG_NO_VM_OVERCOMMIT)) return -EINVAL; + if (XE_IOCTL_DBG(xe, args->flags & DRM_XE_VM_CREATE_FLAG_RESTARTABLE && + (!(args->flags & DRM_XE_VM_CREATE_FLAG_LR_MODE) || + args->flags & DRM_XE_VM_CREATE_FLAG_FAULT_MODE))) + return -EINVAL; + if (args->flags & DRM_XE_VM_CREATE_FLAG_SCRATCH_PAGE) flags |=3D XE_VM_FLAG_SCRATCH_PAGE; if (args->flags & DRM_XE_VM_CREATE_FLAG_LR_MODE) @@ -2100,6 +2191,8 @@ int xe_vm_create_ioctl(struct drm_device *dev, void *= data, flags |=3D XE_VM_FLAG_FAULT_MODE; if (args->flags & DRM_XE_VM_CREATE_FLAG_NO_VM_OVERCOMMIT) flags |=3D XE_VM_FLAG_NO_VM_OVERCOMMIT; + if (args->flags & DRM_XE_VM_CREATE_FLAG_RESTARTABLE) + flags |=3D XE_VM_FLAG_RESTARTABLE; vm =3D xe_vm_create(xe, flags, xef); if (IS_ERR(vm)) diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h index c5b900f38ded..9ee44599cacd 100644 --- a/drivers/gpu/drm/xe/xe_vm.h +++ b/drivers/gpu/drm/xe/xe_vm.h @@ -212,7 +212,8 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, int xe_vm_query_vmas_attrs_ioctl(struct drm_device *dev, void *data, struc= t drm_file *file); int xe_vm_get_property_ioctl(struct drm_device *dev, void *data, struct drm_file *file); - +int xe_vm_restart_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); void xe_vm_close_and_put(struct xe_vm *vm); static inline bool xe_vm_in_fault_mode(struct xe_vm *vm) @@ -237,6 +238,11 @@ static inline bool xe_vm_allow_vm_eviction(struct xe_v= m *vm) !(vm->flags & XE_VM_FLAG_NO_VM_OVERCOMMIT)); } +static inline bool xe_vm_is_restartable(struct xe_vm *vm) +{ + return vm->flags & XE_VM_FLAG_RESTARTABLE; +} + int xe_vm_add_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q= ); void xe_vm_remove_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queu= e *q); diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_ty= pes.h index 635ed29b9a69..7d295c3b8456 100644 --- a/drivers/gpu/drm/xe/xe_vm_types.h +++ b/drivers/gpu/drm/xe/xe_vm_types.h @@ -264,6 +264,7 @@ struct xe_vm { #define XE_VM_FLAG_SET_TILE_ID(tile) FIELD_PREP(GENMASK(7, 6), (tile)->id) #define XE_VM_FLAG_GSC BIT(8) #define XE_VM_FLAG_NO_VM_OVERCOMMIT BIT(9) +#define XE_VM_FLAG_RESTARTABLE BIT(10) unsigned long flags; /** diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index 48e9f1fdb78d..bebb0167bd31 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -85,6 +85,7 @@ extern "C" { * - &DRM_IOCTL_XE_VM_QUERY_MEM_RANGE_ATTRS * - &DRM_IOCTL_XE_EXEC_QUEUE_SET_PROPERTY * - &DRM_IOCTL_XE_VM_GET_PROPERTY + * - &DRM_IOCTL_XE_VM_RESTART */ /* @@ -110,6 +111,7 @@ extern "C" { #define DRM_XE_VM_QUERY_MEM_RANGE_ATTRS 0x0d #define DRM_XE_EXEC_QUEUE_SET_PROPERTY 0x0e #define DRM_XE_VM_GET_PROPERTY 0x0f +#define DRM_XE_VM_RESTART 0x10 /* Must be kept compact -- no holes */ @@ -129,6 +131,7 @@ extern "C" { #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 + DR= M_XE_EXEC_QUEUE_SET_PROPERTY, struct drm_xe_exec_queue_set_property) #define DRM_IOCTL_XE_VM_GET_PROPERTY DRM_IOWR(DRM_COMMAND_BASE + DRM_XE_V= M_GET_PROPERTY, struct drm_xe_vm_get_property) +#define DRM_IOCTL_XE_VM_RESTART DRM_IOW(DRM_COMMAND_BASE + DRM_XE_VM_RES= TART, struct drm_xe_vm_restart) /** * DOC: Xe IOCTL Extensions @@ -985,6 +988,10 @@ struct drm_xe_gem_mmap_offset { * but only during a &DRM_IOCTL_XE_VM_BIND operation with the * %DRM_XE_VM_BIND_FLAG_IMMEDIATE flag set. This may be useful for * user-space naively probing the amount of available memory. + * - %DRM_XE_VM_CREATE_FLAG_RESTARTABLE - Requires also + * DRM_XE_VM_CREATE_FLAG_LR_MODE. Marks the VM as restartable, enabling + * use of &DRM_IOCTL_XE_VM_RESTART to resume the preempt-rebind worker + * after an error has paused it. */ struct drm_xe_vm_create { /** @extensions: Pointer to the first extension struct, if any */ @@ -994,6 +1001,7 @@ struct drm_xe_vm_create { #define DRM_XE_VM_CREATE_FLAG_LR_MODE (1 << 1) #define DRM_XE_VM_CREATE_FLAG_FAULT_MODE (1 << 2) #define DRM_XE_VM_CREATE_FLAG_NO_VM_OVERCOMMIT (1 << 3) +#define DRM_XE_VM_CREATE_FLAG_RESTARTABLE (1 << 4) /** @flags: Flags */ __u32 flags; @@ -2531,8 +2539,44 @@ struct drm_xe_exec_queue_set_property { }; /** - * DOC: Xe DRM RAS + * DOC: DRM_XE_VM_RESTART + * + * Restart a paused VM by queuing its preempt-rebind worker. The VM must = be + * in preempt-fence mode and must currently be paused (i.e. its rebind wor= ker + * was deactivated after a recoverable error such as -ENOMEM or -ENOSPC). + * + * Returns 0 if the rebind worker was successfully queued. Returns -EALRE= ADY + * if the VM is not currently paused. Returns -EINVAL if the VM is not in + * preempt-fence mode or not restartable. * + * An optional @timestamp_ns can be provided to measure the latency between + * event delivery and the point the worker is queued; the driver logs this + * once all sanity checks have passed. + */ + +/** + * struct drm_xe_vm_restart - restart a VM's preempt-rebind worker + * + * Used with %DRM_IOCTL_XE_VM_RESTART. + */ +struct drm_xe_vm_restart { + /** @vm_id: ID of the VM to restart */ + __u32 vm_id; + /** @pad: reserved, must be zero */ + __u32 pad; + /** + * @timestamp_ns: optional CLOCK_MONOTONIC timestamp in nanoseconds. + * When non-zero, the driver logs the delay between this timestamp and + * the point the vm lock is taken, regardless of whether the VM is + * currently paused. Pass zero to disable the logging. + */ + __u64 timestamp_ns; + /** @reserved: reserved, must be zero */ + __u64 reserved; +}; + +/** + * DOC: Xe DRM RAS * The enums and strings defined below map to the attributes of the DRM RA= S Netlink Interface. * Refer to Documentation/netlink/specs/drm_ras.yaml for complete interfac= e specification. * --- drivers/gpu/drm/xe/xe_device.c | 1 + drivers/gpu/drm/xe/xe_vm.c | 99 +++++++++++++++++++++++++++++++- drivers/gpu/drm/xe/xe_vm.h | 8 ++- drivers/gpu/drm/xe/xe_vm_types.h | 1 + include/uapi/drm/xe_drm.h | 46 ++++++++++++++- 5 files changed, 150 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index 51e3a2dd7b22..867d7c55dc03 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -215,6 +215,7 @@ static const struct drm_ioctl_desc xe_ioctls[] =3D { DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(XE_VM_GET_PROPERTY, xe_vm_get_property_ioctl, DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(XE_VM_RESTART, xe_vm_restart_ioctl, DRM_RENDER_ALLOW), }; =20 static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned lon= g arg) diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index 75841f3e9afa..86ed8f31a219 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -563,8 +563,14 @@ static void preempt_rebind_work_func(struct work_struc= t *w) } =20 if (err) { - drm_warn(&vm->xe->drm, "VM worker error: %d\n", err); - xe_vm_kill(vm, true); + if ((err =3D=3D -ENOMEM || err =3D=3D -ENOSPC) && xe_vm_is_restartable(v= m)) { + vm->preempt.rebind_deactivated =3D true; + drm_dbg(&vm->xe->drm, "Rebind deactivated VM on error %pe\n", + ERR_PTR(err)); + } else { + drm_warn(&vm->xe->drm, "VM worker error: %d\n", err); + xe_vm_kill(vm, true); + } } up_write(&vm->lock); =20 @@ -573,6 +579,85 @@ static void preempt_rebind_work_func(struct work_struc= t *w) trace_xe_vm_rebind_worker_exit(vm); } =20 +/** + * xe_vm_restart_ioctl() - Queue the preempt-rebind worker for a paused VM + * @dev: DRM device + * @data: pointer to &struct drm_xe_vm_restart from userspace + * @file: DRM file handle + * + * Looks up the VM identified by @vm_id and, if it is currently paused (its + * rebind worker was deactivated after a recoverable error), clears the pa= used + * state and queues the rebind worker. Only valid for VMs in preempt-fence + * mode. + * + * If @timestamp_ns is non-zero, logs the latency between that timestamp a= nd + * the point the vm lock is taken, regardless of whether the VM was paused. + * + * Return: 0 if the worker was queued, -EALREADY if the VM is not paused, + * -EINVAL if the VM is not in preempt-fence mode or not restartab= le, + * -ENOENT if the VM was not found. + */ +int xe_vm_restart_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct xe_device *xe =3D to_xe_device(dev); + struct xe_file *xef =3D to_xe_file(file); + struct drm_xe_vm_restart *args =3D data; + struct xe_vm *vm; + int err =3D 0; + + if (XE_IOCTL_DBG(xe, args->reserved || args->pad)) + return -EINVAL; + + vm =3D xe_vm_lookup(xef, args->vm_id); + if (XE_IOCTL_DBG(xe, !vm)) + return -ENOENT; + + if (XE_IOCTL_DBG(xe, !xe_vm_in_preempt_fence_mode(vm))) { + xe_vm_put(vm); + return -EINVAL; + } + + if (XE_IOCTL_DBG(xe, !xe_vm_is_restartable(vm))) { + xe_vm_put(vm); + return -EINVAL; + } + + err =3D down_read_interruptible(&vm->lock); + if (err) + goto out; + + if (XE_IOCTL_DBG(xe, xe_vm_is_closed_or_banned(vm))) { + err =3D -ENOENT; + goto out_unlock_read; + } + + if (args->timestamp_ns) { + u64 delay_us =3D (ktime_get_ns() - args->timestamp_ns) / NSEC_PER_USEC; + + drm_dbg(&xe->drm, "VM %u restart latency: %llu us\n", + args->vm_id, delay_us); + } + + err =3D xe_vm_lock(vm, true); + if (err) + goto out_unlock_read; + + if (!vm->preempt.rebind_deactivated) { + err =3D -EALREADY; + goto out_unlock_resv; + } + + xe_vm_reactivate_rebind(vm); +out_unlock_resv: + xe_vm_unlock(vm); +out_unlock_read: + up_read(&vm->lock); +out: + xe_vm_put(vm); + return err; +} + /** * xe_vm_add_fault_entry_pf() - Add pagefault to vm fault list * @vm: The VM. @@ -2049,7 +2134,8 @@ find_ufence_get(struct xe_sync_entry *syncs, u32 num_= syncs) #define ALL_DRM_XE_VM_CREATE_FLAGS (DRM_XE_VM_CREATE_FLAG_SCRATCH_PAGE | \ DRM_XE_VM_CREATE_FLAG_LR_MODE | \ DRM_XE_VM_CREATE_FLAG_FAULT_MODE | \ - DRM_XE_VM_CREATE_FLAG_NO_VM_OVERCOMMIT) + DRM_XE_VM_CREATE_FLAG_NO_VM_OVERCOMMIT | \ + DRM_XE_VM_CREATE_FLAG_RESTARTABLE) =20 int xe_vm_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file) @@ -2092,6 +2178,11 @@ int xe_vm_create_ioctl(struct drm_device *dev, void = *data, args->flags & DRM_XE_VM_CREATE_FLAG_NO_VM_OVERCOMMIT)) return -EINVAL; =20 + if (XE_IOCTL_DBG(xe, args->flags & DRM_XE_VM_CREATE_FLAG_RESTARTABLE && + (!(args->flags & DRM_XE_VM_CREATE_FLAG_LR_MODE) || + args->flags & DRM_XE_VM_CREATE_FLAG_FAULT_MODE))) + return -EINVAL; + if (args->flags & DRM_XE_VM_CREATE_FLAG_SCRATCH_PAGE) flags |=3D XE_VM_FLAG_SCRATCH_PAGE; if (args->flags & DRM_XE_VM_CREATE_FLAG_LR_MODE) @@ -2100,6 +2191,8 @@ int xe_vm_create_ioctl(struct drm_device *dev, void *= data, flags |=3D XE_VM_FLAG_FAULT_MODE; if (args->flags & DRM_XE_VM_CREATE_FLAG_NO_VM_OVERCOMMIT) flags |=3D XE_VM_FLAG_NO_VM_OVERCOMMIT; + if (args->flags & DRM_XE_VM_CREATE_FLAG_RESTARTABLE) + flags |=3D XE_VM_FLAG_RESTARTABLE; =20 vm =3D xe_vm_create(xe, flags, xef); if (IS_ERR(vm)) diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h index c5b900f38ded..9ee44599cacd 100644 --- a/drivers/gpu/drm/xe/xe_vm.h +++ b/drivers/gpu/drm/xe/xe_vm.h @@ -212,7 +212,8 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, int xe_vm_query_vmas_attrs_ioctl(struct drm_device *dev, void *data, struc= t drm_file *file); int xe_vm_get_property_ioctl(struct drm_device *dev, void *data, struct drm_file *file); - +int xe_vm_restart_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); void xe_vm_close_and_put(struct xe_vm *vm); =20 static inline bool xe_vm_in_fault_mode(struct xe_vm *vm) @@ -237,6 +238,11 @@ static inline bool xe_vm_allow_vm_eviction(struct xe_v= m *vm) !(vm->flags & XE_VM_FLAG_NO_VM_OVERCOMMIT)); } =20 +static inline bool xe_vm_is_restartable(struct xe_vm *vm) +{ + return vm->flags & XE_VM_FLAG_RESTARTABLE; +} + int xe_vm_add_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q= ); void xe_vm_remove_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queu= e *q); =20 diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_ty= pes.h index 635ed29b9a69..7d295c3b8456 100644 --- a/drivers/gpu/drm/xe/xe_vm_types.h +++ b/drivers/gpu/drm/xe/xe_vm_types.h @@ -264,6 +264,7 @@ struct xe_vm { #define XE_VM_FLAG_SET_TILE_ID(tile) FIELD_PREP(GENMASK(7, 6), (tile)->id) #define XE_VM_FLAG_GSC BIT(8) #define XE_VM_FLAG_NO_VM_OVERCOMMIT BIT(9) +#define XE_VM_FLAG_RESTARTABLE BIT(10) unsigned long flags; =20 /** diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index 48e9f1fdb78d..bebb0167bd31 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -85,6 +85,7 @@ extern "C" { * - &DRM_IOCTL_XE_VM_QUERY_MEM_RANGE_ATTRS * - &DRM_IOCTL_XE_EXEC_QUEUE_SET_PROPERTY * - &DRM_IOCTL_XE_VM_GET_PROPERTY + * - &DRM_IOCTL_XE_VM_RESTART */ =20 /* @@ -110,6 +111,7 @@ extern "C" { #define DRM_XE_VM_QUERY_MEM_RANGE_ATTRS 0x0d #define DRM_XE_EXEC_QUEUE_SET_PROPERTY 0x0e #define DRM_XE_VM_GET_PROPERTY 0x0f +#define DRM_XE_VM_RESTART 0x10 =20 /* Must be kept compact -- no holes */ =20 @@ -129,6 +131,7 @@ extern "C" { #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 + DR= M_XE_EXEC_QUEUE_SET_PROPERTY, struct drm_xe_exec_queue_set_property) #define DRM_IOCTL_XE_VM_GET_PROPERTY DRM_IOWR(DRM_COMMAND_BASE + DRM_XE_V= M_GET_PROPERTY, struct drm_xe_vm_get_property) +#define DRM_IOCTL_XE_VM_RESTART DRM_IOW(DRM_COMMAND_BASE + DRM_XE_VM_RES= TART, struct drm_xe_vm_restart) =20 /** * DOC: Xe IOCTL Extensions @@ -985,6 +988,10 @@ struct drm_xe_gem_mmap_offset { * but only during a &DRM_IOCTL_XE_VM_BIND operation with the * %DRM_XE_VM_BIND_FLAG_IMMEDIATE flag set. This may be useful for * user-space naively probing the amount of available memory. + * - %DRM_XE_VM_CREATE_FLAG_RESTARTABLE - Requires also + * DRM_XE_VM_CREATE_FLAG_LR_MODE. Marks the VM as restartable, enabling + * use of &DRM_IOCTL_XE_VM_RESTART to resume the preempt-rebind worker + * after an error has paused it. */ struct drm_xe_vm_create { /** @extensions: Pointer to the first extension struct, if any */ @@ -994,6 +1001,7 @@ struct drm_xe_vm_create { #define DRM_XE_VM_CREATE_FLAG_LR_MODE (1 << 1) #define DRM_XE_VM_CREATE_FLAG_FAULT_MODE (1 << 2) #define DRM_XE_VM_CREATE_FLAG_NO_VM_OVERCOMMIT (1 << 3) +#define DRM_XE_VM_CREATE_FLAG_RESTARTABLE (1 << 4) /** @flags: Flags */ __u32 flags; =20 @@ -2531,8 +2539,44 @@ struct drm_xe_exec_queue_set_property { }; =20 /** - * DOC: Xe DRM RAS + * DOC: DRM_XE_VM_RESTART + * + * Restart a paused VM by queuing its preempt-rebind worker. The VM must = be + * in preempt-fence mode and must currently be paused (i.e. its rebind wor= ker + * was deactivated after a recoverable error such as -ENOMEM or -ENOSPC). + * + * Returns 0 if the rebind worker was successfully queued. Returns -EALRE= ADY + * if the VM is not currently paused. Returns -EINVAL if the VM is not in + * preempt-fence mode or not restartable. * + * An optional @timestamp_ns can be provided to measure the latency between + * event delivery and the point the worker is queued; the driver logs this + * once all sanity checks have passed. + */ + +/** + * struct drm_xe_vm_restart - restart a VM's preempt-rebind worker + * + * Used with %DRM_IOCTL_XE_VM_RESTART. + */ +struct drm_xe_vm_restart { + /** @vm_id: ID of the VM to restart */ + __u32 vm_id; + /** @pad: reserved, must be zero */ + __u32 pad; + /** + * @timestamp_ns: optional CLOCK_MONOTONIC timestamp in nanoseconds. + * When non-zero, the driver logs the delay between this timestamp and + * the point the vm lock is taken, regardless of whether the VM is + * currently paused. Pass zero to disable the logging. + */ + __u64 timestamp_ns; + /** @reserved: reserved, must be zero */ + __u64 reserved; +}; + +/** + * DOC: Xe DRM RAS * The enums and strings defined below map to the attributes of the DRM RA= S Netlink Interface. * Refer to Documentation/netlink/specs/drm_ras.yaml for complete interfac= e specification. * --=20 2.54.0 From nobody Thu Jun 25 05:23:11 2026 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2181D3438BA for ; Fri, 12 Jun 2026 13:54:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781272463; cv=none; b=J46GZUCZ9lH1C5X2IGrn8z3a5BG2NyUbD6tFloD5Np380GB+HWeqpME4UnM+CWGsLe2Zm2SJSO+J4OAb9OThWtVfd7TV5guQMGZdWTtw1af2kCVwwhm3sKVZhQ2qFlA/IEAbiOSyDQ5yiGGBeZf8Iojwb+UnQGG7NPjU2dG+iC8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781272463; c=relaxed/simple; bh=kILQ85veuIrcULzo1G8jHYeL8gmTmoLkPT7LMDIHXQ0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=taONNur4Ln2xgGU3epjwrCXcFbQKwDrrXiB8fqLxBgwiHgtcqyhtCtBpdK+AzU85qPO4ZYC0Ic4YgNW9gE4K0VbYGcOev9Nfauu6gAQ+6jj1SuQN/x/yTiyqMPUqziYgA2MZaNwSIZEvqdPqi4xVEirqTzL6oHdm2+fG6Ews0KE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com; spf=pass smtp.mailfrom=linux.intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=KOpV0D7I; arc=none smtp.client-ip=198.175.65.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="KOpV0D7I" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1781272462; x=1812808462; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=kILQ85veuIrcULzo1G8jHYeL8gmTmoLkPT7LMDIHXQ0=; b=KOpV0D7IUx4+tCpANCtCMykhLyvIVAUfMmL1OOvwaaLK9vWuzDROrnKg hilhKzrZyEezYNVcsve/BHCVzlq+EYMwn4o8Lxt9uPMgeDCbcbyiYGC6P eTOYlJq9KpDqqASgA2xgd44Htpcc2tSxZnV0rjTvD8MrpI7boz48/jPWB c/jH5b1N0dbGyH9WyKh35DPfH6Q+eMTpa2pndW1KHCisuj9ldDfYIxrdb KYG6OcNcqvygg/20/0l1hdtcuAUADE+1qppVvdo9zLuitX8PlX7Of9ans UTuhUYVeFsPaRXCNZCwwQBMybljX+fHcv3bxOF5nIJC7CaweyxltdHY/S w==; X-CSE-ConnectionGUID: TKRP5JP9TRiCBExWt4sZqQ== X-CSE-MsgGUID: UU+NyVO1RS21xMRjxEmVjA== X-IronPort-AV: E=McAfee;i="6800,10657,11813"; a="82163310" X-IronPort-AV: E=Sophos;i="6.24,200,1774335600"; d="scan'208";a="82163310" Received: from fmviesa006.fm.intel.com ([10.60.135.146]) by orvoesa110.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 12 Jun 2026 06:54:22 -0700 X-CSE-ConnectionGUID: 3CyBZ6NlS2WgezIvRIyQvw== X-CSE-MsgGUID: cle2O9uAQtGl90O/1adiGw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.24,200,1774335600"; d="scan'208";a="242446553" Received: from slindbla-desk.ger.corp.intel.com (HELO fedora) ([10.245.245.68]) by fmviesa006-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 12 Jun 2026 06:54:18 -0700 From: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= To: intel-xe@lists.freedesktop.org Cc: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= , Matthew Brost , Maarten Lankhorst , Michal Mrozek , John Falkowski , Rodrigo Vivi , Lahtinen Joonas , David Howells , Christian Brauner , Kees Cook , Davidlohr Bueso , =?UTF-8?q?Christian=20K=C3=B6nig?= , Dave Airlie , Simona Vetter , dri-devel@lists.freedesktop.org, LMKL Subject: [PATCH 2/4] drm/xe: Add fault injection for rebind worker -ENOSPC Date: Fri, 12 Jun 2026 15:53:38 +0200 Message-ID: <20260612135340.116100-3-thomas.hellstrom@linux.intel.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260612135340.116100-1-thomas.hellstrom@linux.intel.com> References: <20260612135340.116100-1-thomas.hellstrom@linux.intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Add fault injection support using the kernel fault injection infrastructure to inject -ENOSPC early in the success path of preempt_rebind_work_func(), before xe_svm_notifier_lock() is taken, testing the error handling paths without interference from real resource exhaustion. Injection is restricted to restartable VMs. When triggered, the worker deactivates the VM (rebind_deactivated). Upcoming patches will then also post an error event to userspace. Enable via debugfs: echo 1 > /sys/kernel/debug/dri/0/fail_rebind/times echo 100 > /sys/kernel/debug/dri/0/fail_rebind/probability Assisted-by: GitHub_Copilot:claude-sonnet-4.6 Signed-off-by: Thomas Hellstr=C3=B6m --- drivers/gpu/drm/xe/xe_debugfs.c | 4 +++- drivers/gpu/drm/xe/xe_vm.c | 32 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/xe/xe_vm.h | 5 +++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_debugfs.c b/drivers/gpu/drm/xe/xe_debugf= s.c index 22b471303984..1a92c52ccd83 100644 --- a/drivers/gpu/drm/xe/xe_debugfs.c +++ b/drivers/gpu/drm/xe/xe_debugfs.c @@ -35,8 +35,8 @@ #ifdef CONFIG_DRM_XE_DEBUG #include "xe_bo_evict.h" #include "xe_migrate.h" -#include "xe_vm.h" #endif +#include "xe_vm.h" =20 DECLARE_FAULT_ATTR(gt_reset_failure); DECLARE_FAULT_ATTR(inject_csc_hw_error); @@ -612,6 +612,8 @@ void xe_debugfs_register(struct xe_device *xe) =20 fault_create_debugfs_attr("fail_gt_reset", root, >_reset_failure); =20 + xe_vm_debugfs_register(root); + if (IS_SRIOV_PF(xe)) xe_sriov_pf_debugfs_register(xe, root); else if (IS_SRIOV_VF(xe)) diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index 86ed8f31a219..b69a2e5bd9c9 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -18,6 +18,9 @@ #include #include #include +#ifdef CONFIG_DEBUG_FS +#include +#endif =20 #include =20 @@ -43,6 +46,17 @@ #include "xe_vm_madvise.h" #include "xe_wa.h" =20 +#ifdef CONFIG_FAULT_INJECTION +static DECLARE_FAULT_ATTR(rebind_enospc); + +static void xe_vm_register_fault_attrs(struct dentry *root) +{ + fault_create_debugfs_attr("fail_rebind", root, &rebind_enospc); +} +#else +static inline void xe_vm_register_fault_attrs(struct dentry *root) {} +#endif + static struct drm_gem_object *xe_vm_obj(struct xe_vm *vm) { return vm->gpuvm.r_obj; @@ -529,6 +543,13 @@ static void preempt_rebind_work_func(struct work_struc= t *w) goto out_unlock; } =20 +#ifdef CONFIG_FAULT_INJECTION + if (xe_vm_is_restartable(vm) && should_fail(&rebind_enospc, 1)) { + err =3D -ENOSPC; + goto out_unlock; + } +#endif + #define retry_required(__tries, __vm) \ (IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT) ? \ (!(__tries)++ || __xe_vm_userptr_needs_repin(__vm)) : \ @@ -5042,3 +5063,14 @@ void xe_vm_remove_exec_queue(struct xe_vm *vm, struc= t xe_exec_queue *q) } up_write(&vm->exec_queues.lock); } + +#ifdef CONFIG_DEBUG_FS +/** + * xe_vm_debugfs_register() - Register xe_vm debugfs entries + * @root: debugfs root dentry for this device + */ +void xe_vm_debugfs_register(struct dentry *root) +{ + xe_vm_register_fault_attrs(root); +} +#endif diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h index 9ee44599cacd..0f9a38d97bf6 100644 --- a/drivers/gpu/drm/xe/xe_vm.h +++ b/drivers/gpu/drm/xe/xe_vm.h @@ -216,6 +216,11 @@ int xe_vm_restart_ioctl(struct drm_device *dev, void *= data, struct drm_file *file); void xe_vm_close_and_put(struct xe_vm *vm); =20 +#ifdef CONFIG_DEBUG_FS +struct dentry; +void xe_vm_debugfs_register(struct dentry *root); +#endif + static inline bool xe_vm_in_fault_mode(struct xe_vm *vm) { return vm->flags & XE_VM_FLAG_FAULT_MODE; --=20 2.54.0 From nobody Thu Jun 25 05:23:11 2026 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B5605348C4E for ; Fri, 12 Jun 2026 13:54:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781272467; cv=none; b=BdjUnTrPjbUvulTryhPk+B1tLqfc7Rkqtl/66XoMWdxEeuwuK9qjm+jUswLsV/S0J3FubugxAV+XgBVzsCsvQxf3LFqkBGXyPXRKtJB1AMtjlOWAqpzKcinrNPiXImP5h9Dtg9HVeb//3DhoCWEiQ7KpT2/7/IIcVqF6qwom/5k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781272467; c=relaxed/simple; bh=mpmpGW+MCEfXXyWOnroMWvjhaZ0eijkdkHSn/yk15Vo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=dzB17yS4zL4MouQz1U3RTpoUssZMM/EkVGwUHG/uK1IORcA4ykdVmmWPQFjbVrzrsgij6YZu9CQse1UoRxLotj1rCtUzj+Db+NoYPorMBqeTecBhkJhOQ+V0pIw4CmDevT4YosEZ4PJcQfGEBkntqa6+XuSFufTJJOxQqVRXbcs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com; spf=pass smtp.mailfrom=linux.intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=Vh/onb7d; arc=none smtp.client-ip=198.175.65.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="Vh/onb7d" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1781272466; x=1812808466; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=mpmpGW+MCEfXXyWOnroMWvjhaZ0eijkdkHSn/yk15Vo=; b=Vh/onb7d5LXcB9xJkl/NLOsrQi/76yrFLlT2AS4pn262JlkBixGYSle6 xYKk/NLwm40sMvE5b2gJZLH7X0oU7vuBzJA5KEZMvWoWw2q0j4T5oPXff 6GG4FFUeKQJdn8s1V31fhpaBabLVAKwH2qKEwODQCDpIPS+23M6COc4Zr chH8eFGzJgLVNy8tktbnYVNc33PScpfxFnkoBjHYD6apqZPNgL7DsRCgn umvnEjcZ5oP816VkgFodk46k+EinCv3IklNSyKENCtcvtkdc83mUqkSjY qVoB50hHFasgdI4zVUHUUZryUEwzOTkC6UXkPJyD+MCGsWoTmMLGDEgwP w==; X-CSE-ConnectionGUID: oBo39dSJR66CjrdgPMUQFw== X-CSE-MsgGUID: mCkLDeYDR/2fJqKEXbWjdg== X-IronPort-AV: E=McAfee;i="6800,10657,11813"; a="82163319" X-IronPort-AV: E=Sophos;i="6.24,200,1774335600"; d="scan'208";a="82163319" Received: from fmviesa006.fm.intel.com ([10.60.135.146]) by orvoesa110.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 12 Jun 2026 06:54:26 -0700 X-CSE-ConnectionGUID: C77P05NFQuqrguWisOlbUA== X-CSE-MsgGUID: 09WONNwoR3itXxQQQ50Qsg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.24,200,1774335600"; d="scan'208";a="242446577" Received: from slindbla-desk.ger.corp.intel.com (HELO fedora) ([10.245.245.68]) by fmviesa006-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 12 Jun 2026 06:54:22 -0700 From: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= To: intel-xe@lists.freedesktop.org Cc: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= , Matthew Brost , Maarten Lankhorst , Michal Mrozek , John Falkowski , Rodrigo Vivi , Lahtinen Joonas , David Howells , Christian Brauner , Kees Cook , Davidlohr Bueso , =?UTF-8?q?Christian=20K=C3=B6nig?= , Dave Airlie , Simona Vetter , dri-devel@lists.freedesktop.org, LMKL Subject: [PATCH 3/4] watch_queue: Add a DRM_XE_NOTIFY watch type and export init_watch() Date: Fri, 12 Jun 2026 15:53:39 +0200 Message-ID: <20260612135340.116100-4-thomas.hellstrom@linux.intel.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260612135340.116100-1-thomas.hellstrom@linux.intel.com> References: <20260612135340.116100-1-thomas.hellstrom@linux.intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Add a DRM_XE_NOTIFY watch type for asynchronous error notifications from the DRM_XE kernel module. The reason for not registering a DRM - wide notification type is that the notification type is 24 bits wide, the subtype is only 8, If this is a concern one could define the DRM - wide subtypes to be per driver, not common across DRM. Also export the init_watch() function for use from kernel drivers. Use EXPORT_SYMBOL() to align with other exports from the same file. Assisted-by: GitHub_Copilot:claude-sonnet-4.6 Signed-off-by: Thomas Hellstr=C3=B6m --- include/uapi/drm/xe_drm.h | 4 ++-- include/uapi/linux/watch_queue.h | 3 ++- kernel/watch_queue.c | 13 ++++++++++--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index bebb0167bd31..8d5e3f06b8d4 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -2550,8 +2550,8 @@ struct drm_xe_exec_queue_set_property { * preempt-fence mode or not restartable. * * An optional @timestamp_ns can be provided to measure the latency between - * event delivery and the point the worker is queued; the driver logs this - * once all sanity checks have passed. + * event delivery and locking; the driver logs this regardless of whether = the + * VM was paused. */ =20 /** diff --git a/include/uapi/linux/watch_queue.h b/include/uapi/linux/watch_qu= eue.h index c3d8320b5d3a..c800c153989d 100644 --- a/include/uapi/linux/watch_queue.h +++ b/include/uapi/linux/watch_queue.h @@ -14,7 +14,8 @@ enum watch_notification_type { WATCH_TYPE_META =3D 0, /* Special record */ WATCH_TYPE_KEY_NOTIFY =3D 1, /* Key change event notification */ - WATCH_TYPE__NR =3D 2 + WATCH_TYPE_DRM_XE_NOTIFY =3D 2, /* DRM device event notification */ + WATCH_TYPE__NR =3D 3 }; =20 enum watch_meta_notification_subtype { diff --git a/kernel/watch_queue.c b/kernel/watch_queue.c index 538520861e8b..701b5c388808 100644 --- a/kernel/watch_queue.c +++ b/kernel/watch_queue.c @@ -445,11 +445,17 @@ static void put_watch(struct watch *watch) } =20 /** - * init_watch - Initialise a watch + * init_watch() - Initialise a watch subscription * @watch: The watch to initialise. - * @wqueue: The queue to assign. + * @wqueue: The watch queue (notification pipe) to associate with the watc= h. * - * Initialise a watch and set the watch queue. + * Initialise a newly allocated watch object and associate it with @wqueue. + * The caller must subsequently set @watch->id and @watch->info_id before + * calling add_watch_to_object() to subscribe the watch to a notification + * source. + * + * The watch queue reference is held internally; call put_watch_queue() if + * the watch is not successfully passed to add_watch_to_object(). */ void init_watch(struct watch *watch, struct watch_queue *wqueue) { @@ -458,6 +464,7 @@ void init_watch(struct watch *watch, struct watch_queue= *wqueue) INIT_HLIST_NODE(&watch->queue_node); rcu_assign_pointer(watch->queue, wqueue); } +EXPORT_SYMBOL(init_watch); =20 static int add_one_watch(struct watch *watch, struct watch_list *wlist, st= ruct watch_queue *wqueue) { --=20 2.54.0 From nobody Thu Jun 25 05:23:11 2026 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9239F31F99F for ; Fri, 12 Jun 2026 13:54:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781272471; cv=none; b=bFbFXD43bmcKexX/7W8DbruEmpLeyZEgPVCx8yJvuXqbk6mjW4AhACpxaSCYKwswGEK/U3Ba+1eBBhABUAC9as5rdK2nA8u4S35Cj/8LmObp0D6IzUjQfk2ZW5moa1z1xa23HiW5wCJR57Xo894wA5V9Qk8Gb1IZ+r8yrhW2ezY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781272471; c=relaxed/simple; bh=x0WBenL+tYyEWxbwyAiUMsF2Sul7xYWiNZv7X+Mi0zg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Tnk2SATP6YBlnUUhnmKSq7BrpwVGNXcOIfHDFTcuMjBmktQ+Vygs4SMh0rFl77N1isHjlk1CuaCRKelR11AkobB/RTQp5LixPyeyuDlQU/O9nbz6oHQZogOCaFhLXW3MlvNJMpOviYq2utXehhchBYm9k5i/HwhWVYdUSFt2N1g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com; spf=pass smtp.mailfrom=linux.intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=IwA3Vtka; arc=none smtp.client-ip=198.175.65.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="IwA3Vtka" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1781272470; x=1812808470; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=x0WBenL+tYyEWxbwyAiUMsF2Sul7xYWiNZv7X+Mi0zg=; b=IwA3Vtka3Nru8qDFotg72QmkOEBvakfsVveFqrGBcnBbbeltG8I5Zqc4 Nq5Zg/lzXOx8CgW8YSb/UUytoleLcIrlspI9FkPobs36cjcFyIkvaXFfB IYFYNPKha4TN/ga+6v+XB5oK3HV6xrYAIT3lb8SCKuhfzs5obnfoaqhO3 U64+sjkaJGCAIW7/VHKnlQtZMyD023WLD9RsDV4NIGXsg+xR1EgCNBqYa tzY+o6XuJrxBpGcgQVvsrJgFinJAjqFOdhcgNKj/LWg3RsCIH9qUOGXdm LkVdYWEMvWPk/aEhqzfihwVjT+lgcf303aoT6yBAtorAnkKjCR3pgS4vz A==; X-CSE-ConnectionGUID: PtJStLQFSV6Yo0QFppXQZA== X-CSE-MsgGUID: zyQBdkiDT86f79vaHQ6Qjg== X-IronPort-AV: E=McAfee;i="6800,10657,11813"; a="82163327" X-IronPort-AV: E=Sophos;i="6.24,200,1774335600"; d="scan'208";a="82163327" Received: from fmviesa006.fm.intel.com ([10.60.135.146]) by orvoesa110.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 12 Jun 2026 06:54:30 -0700 X-CSE-ConnectionGUID: QJv0tLjURmi95u1C2Gmu3w== X-CSE-MsgGUID: VqeW38znQPysJkw45T3XMw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.24,200,1774335600"; d="scan'208";a="242446604" Received: from slindbla-desk.ger.corp.intel.com (HELO fedora) ([10.245.245.68]) by fmviesa006-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 12 Jun 2026 06:54:26 -0700 From: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= To: intel-xe@lists.freedesktop.org Cc: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= , Matthew Brost , Maarten Lankhorst , Michal Mrozek , John Falkowski , Rodrigo Vivi , Lahtinen Joonas , David Howells , Christian Brauner , Kees Cook , Davidlohr Bueso , =?UTF-8?q?Christian=20K=C3=B6nig?= , Dave Airlie , Simona Vetter , dri-devel@lists.freedesktop.org, LMKL Subject: [PATCH 4/4] drm/xe: Add watch_queue-based device event notification Date: Fri, 12 Jun 2026 15:53:40 +0200 Message-ID: <20260612135340.116100-5-thomas.hellstrom@linux.intel.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260612135340.116100-1-thomas.hellstrom@linux.intel.com> References: <20260612135340.116100-1-thomas.hellstrom@linux.intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Add a watch_queue notification channel tied to struct xe_file so that userspace can subscribe to asynchronous GPU device events via the general kernel notification mechanism. Introduce DRM_IOCTL_XE_WATCH_QUEUE to let userspace subscribe a notification pipe (opened with pipe2(O_NOTIFICATION_PIPE)) to the device event stream. Embed the watch_id field (0-255) in the WATCH_INFO_ID field of every notification, allowing multiple watches to share a single pipe and be told apart by the reader. Deliver notifications as struct drm_xe_watch_notification records, with type always set to WATCH_TYPE_DRM_XE_NOTIFY and subtype drawn from enum drm_xe_watch_event. Define DRM_XE_WATCH_EVENT_VM_ERR as the first event, posted by the preempt-rebind worker when a VM encounters an unrecoverable error. Expose xe_watch_queue_post_vm_err_event() as the in-kernel posting API. Add event definitions in a separate uapi header, . The main reason is that the header needs to include which in turn includes which may conflict with the system . Hence user-space must pay special attention when including this file. Assisted-by: GitHub_Copilot:claude-sonnet-4.6 Signed-off-by: Thomas Hellstr=C3=B6m --- MAINTAINERS | 1 + drivers/gpu/drm/xe/Kconfig | 1 + drivers/gpu/drm/xe/Makefile | 1 + drivers/gpu/drm/xe/xe_device.c | 7 ++ drivers/gpu/drm/xe/xe_device_types.h | 6 ++ drivers/gpu/drm/xe/xe_vm.c | 4 + drivers/gpu/drm/xe/xe_vm_types.h | 2 + drivers/gpu/drm/xe/xe_watch_queue.c | 111 +++++++++++++++++++++++++++ drivers/gpu/drm/xe/xe_watch_queue.h | 20 +++++ include/uapi/drm/xe_drm.h | 45 +++++++++++ include/uapi/drm/xe_drm_events.h | 62 +++++++++++++++ 11 files changed, 260 insertions(+) create mode 100644 drivers/gpu/drm/xe/xe_watch_queue.c create mode 100644 drivers/gpu/drm/xe/xe_watch_queue.h create mode 100644 include/uapi/drm/xe_drm_events.h diff --git a/MAINTAINERS b/MAINTAINERS index 8c0d9965c636..b7e02cfa692b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12900,6 +12900,7 @@ F: Documentation/gpu/xe/ F: drivers/gpu/drm/xe/ F: include/drm/intel/ F: include/uapi/drm/xe_drm.h +F: include/uapi/drm/xe_drm_events.h =20 INTEL ELKHART LAKE PSE I/O DRIVER M: Raag Jadav diff --git a/drivers/gpu/drm/xe/Kconfig b/drivers/gpu/drm/xe/Kconfig index 4d7dcaff2b91..dbdc2fb49c53 100644 --- a/drivers/gpu/drm/xe/Kconfig +++ b/drivers/gpu/drm/xe/Kconfig @@ -25,6 +25,7 @@ config DRM_XE select DRM_MIPI_DSI select RELAY select IRQ_WORK + select WATCH_QUEUE # xe depends on ACPI_VIDEO when ACPI is enabled # but for select to work, need to select ACPI_VIDEO's dependencies, ick select BACKLIGHT_CLASS_DEVICE if ACPI diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index 8e7b146880f4..fc8b4023a044 100644 --- a/drivers/gpu/drm/xe/Makefile +++ b/drivers/gpu/drm/xe/Makefile @@ -150,6 +150,7 @@ xe-y +=3D xe_bb.o \ xe_vsec.o \ xe_wa.o \ xe_wait_user_fence.o \ + xe_watch_queue.o \ xe_wopcm.o =20 xe-$(CONFIG_I2C) +=3D xe_i2c.o diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index 867d7c55dc03..788ef2fbd6e5 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -9,6 +9,7 @@ #include #include #include +#include =20 #include #include @@ -77,6 +78,7 @@ #include "xe_vsec.h" #include "xe_wait_user_fence.h" #include "xe_wa.h" +#include "xe_watch_queue.h" =20 #include #include @@ -112,6 +114,8 @@ static int xe_file_open(struct drm_device *dev, struct = drm_file *file) file->driver_priv =3D xef; kref_init(&xef->refcount); =20 + init_watch_list(&xef->watch_list, NULL); + task =3D get_pid_task(rcu_access_pointer(file->pid), PIDTYPE_PID); if (task) { xef->process_name =3D kstrdup(task->comm, GFP_KERNEL); @@ -126,6 +130,8 @@ static void xe_file_destroy(struct kref *ref) { struct xe_file *xef =3D container_of(ref, struct xe_file, refcount); =20 + remove_watch_from_object(&xef->watch_list, NULL, 0, true); + xa_destroy(&xef->exec_queue.xa); mutex_destroy(&xef->exec_queue.lock); xa_destroy(&xef->vm.xa); @@ -216,6 +222,7 @@ static const struct drm_ioctl_desc xe_ioctls[] =3D { DRM_IOCTL_DEF_DRV(XE_VM_GET_PROPERTY, xe_vm_get_property_ioctl, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(XE_VM_RESTART, xe_vm_restart_ioctl, DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(XE_WATCH_QUEUE, xe_watch_queue_ioctl, DRM_RENDER_ALLOW), }; =20 static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned lon= g arg) diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_d= evice_types.h index 32dd2ffbc796..ca726ada30a7 100644 --- a/drivers/gpu/drm/xe/xe_device_types.h +++ b/drivers/gpu/drm/xe/xe_device_types.h @@ -11,6 +11,7 @@ #include #include #include +#include =20 #include "xe_devcoredump_types.h" #include "xe_drm_ras_types.h" @@ -632,6 +633,11 @@ struct xe_file { =20 /** @refcount: ref count of this xe file */ struct kref refcount; + +#ifdef CONFIG_WATCH_QUEUE + /** @watch_list: per-file notification source for device events */ + struct watch_list watch_list; +#endif }; =20 #endif diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index b69a2e5bd9c9..232de0d948d2 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,7 @@ #include "xe_trace_bo.h" #include "xe_vm_madvise.h" #include "xe_wa.h" +#include "xe_watch_queue.h" =20 #ifdef CONFIG_FAULT_INJECTION static DECLARE_FAULT_ATTR(rebind_enospc); @@ -584,6 +586,7 @@ static void preempt_rebind_work_func(struct work_struct= *w) } =20 if (err) { + xe_watch_queue_post_vm_err_event(vm->xef, vm->id, err); if ((err =3D=3D -ENOMEM || err =3D=3D -ENOSPC) && xe_vm_is_restartable(v= m)) { vm->preempt.rebind_deactivated =3D true; drm_dbg(&vm->xe->drm, "Rebind deactivated VM on error %pe\n", @@ -2229,6 +2232,7 @@ int xe_vm_create_ioctl(struct drm_device *dev, void *= data, if (err) goto err_close_and_put; =20 + vm->id =3D id; args->vm_id =3D id; =20 return 0; diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_ty= pes.h index 7d295c3b8456..19a673099588 100644 --- a/drivers/gpu/drm/xe/xe_vm_types.h +++ b/drivers/gpu/drm/xe/xe_vm_types.h @@ -407,6 +407,8 @@ struct xe_vm { bool batch_invalidate_tlb; /** @xef: Xe file handle for tracking this VM's drm client */ struct xe_file *xef; + /** @id: The id of the VM in the VM table of @xef. */ + u32 id; }; =20 /** struct xe_vma_op_map - VMA map operation */ diff --git a/drivers/gpu/drm/xe/xe_watch_queue.c b/drivers/gpu/drm/xe/xe_wa= tch_queue.c new file mode 100644 index 000000000000..32763591075b --- /dev/null +++ b/drivers/gpu/drm/xe/xe_watch_queue.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright =C2=A9 2026 Intel Corporation + */ + +#include +#include +#include + +#include +#include + +#include "xe_device.h" +#include "xe_device_types.h" +#include "xe_macros.h" +#include "xe_watch_queue.h" + +/** + * struct xe_watch_notification_vm_err - kernel-side VM error event notifi= cation + * @base: common watch notification header; type is %WATCH_TYPE_DRM_XE_NOT= IFY, + * subtype is %DRM_XE_WATCH_EVENT_VM_ERR + * @vm_id: ID of the VM that hit error + * @error_code: error code describing the error condition (negative errno) + * @timestamp_ns: CLOCK_MONOTONIC timestamp in nanoseconds at the point the + * error was detected + * + * Layout mirrors &struct drm_xe_watch_notification_vm_err. + */ +struct xe_watch_notification_vm_err { + struct watch_notification base; + u32 vm_id; + s32 error_code; + u64 timestamp_ns; +}; + +/** + * xe_watch_queue_ioctl() - Subscribe a pipe to per-file device event noti= fications + * @dev: DRM device + * @data: pointer to &struct drm_xe_watch_queue from userspace + * @file: DRM file handle of the subscribing process + * + * Subscribes a notification pipe to receive Xe device events for the call= ing + * process's file handle. Only events scoped to this file (e.g. VM error = on a + * VM owned by this file) are delivered. The pipe must have been opened w= ith + * O_NOTIFICATION_PIPE and sized with %IOC_WATCH_QUEUE_SET_SIZE before cal= ling + * this IOCTL. + * + * Return: 0 on success, negative errno on failure. + */ +int xe_watch_queue_ioctl(struct drm_device *dev, void *data, struct drm_fi= le *file) +{ + struct xe_file *xef =3D file->driver_priv; + struct xe_device *xe =3D to_xe_device(dev); + struct drm_xe_watch_queue *args =3D data; + struct watch_queue *wqueue; + struct watch *watch; + int ret; + + if (XE_IOCTL_DBG(xe, args->flags || args->pad)) + return -EINVAL; + if (XE_IOCTL_DBG(xe, args->watch_id > 0xff)) + return -EINVAL; + + wqueue =3D get_watch_queue(args->fd); + if (XE_IOCTL_DBG(xe, IS_ERR(wqueue))) + return PTR_ERR(wqueue); + + watch =3D kzalloc_obj(*watch, GFP_KERNEL | __GFP_ACCOUNT); + if (XE_IOCTL_DBG(xe, !watch)) { + ret =3D -ENOMEM; + goto out_put_queue; + } + + init_watch(watch, wqueue); + watch->id =3D 0; + watch->info_id =3D (u32)args->watch_id << WATCH_INFO_ID__SHIFT; + + ret =3D add_watch_to_object(watch, &xef->watch_list); + if (XE_IOCTL_DBG(xe, ret)) + kfree(watch); + +out_put_queue: + put_watch_queue(wqueue); + return ret; +} + +/** + * xe_watch_queue_post_vm_err_event() - Post a VM error event + * @xef: xe file handle that owns the VM + * @vm_id: userspace ID of the VM that hit error + * @error_code: error code describing the error condition (negative errno) + * + * Posts a %DRM_XE_WATCH_EVENT_VM_ERR notification carrying @vm_id and + * @error_code to every pipe that @xef has subscribed via + * %DRM_IOCTL_XE_WATCH_QUEUE. Only the owning process is notified, + * preventing information leaks to other clients. + */ +void xe_watch_queue_post_vm_err_event(struct xe_file *xef, u32 vm_id, + int error_code) +{ + struct xe_watch_notification_vm_err n =3D {}; + + n.base.type =3D WATCH_TYPE_DRM_XE_NOTIFY; + n.base.subtype =3D DRM_XE_WATCH_EVENT_VM_ERR; + n.base.info =3D watch_sizeof(struct xe_watch_notification_vm_err); + n.vm_id =3D vm_id; + n.error_code =3D error_code; + n.timestamp_ns =3D ktime_get_ns(); + + post_watch_notification(&xef->watch_list, &n.base, current_cred(), 0); +} diff --git a/drivers/gpu/drm/xe/xe_watch_queue.h b/drivers/gpu/drm/xe/xe_wa= tch_queue.h new file mode 100644 index 000000000000..ad199ee68205 --- /dev/null +++ b/drivers/gpu/drm/xe/xe_watch_queue.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright =C2=A9 2026 Intel Corporation + */ + +#ifndef _XE_WATCH_QUEUE_H_ +#define _XE_WATCH_QUEUE_H_ + +#include + +struct drm_device; +struct drm_file; +struct xe_file; + +int xe_watch_queue_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); +void xe_watch_queue_post_vm_err_event(struct xe_file *xef, u32 vm_id, + int error_code); + +#endif /* _XE_WATCH_QUEUE_H_ */ diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index 8d5e3f06b8d4..0083dd712f7e 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -86,6 +86,7 @@ extern "C" { * - &DRM_IOCTL_XE_EXEC_QUEUE_SET_PROPERTY * - &DRM_IOCTL_XE_VM_GET_PROPERTY * - &DRM_IOCTL_XE_VM_RESTART + * - &DRM_IOCTL_XE_WATCH_QUEUE */ =20 /* @@ -112,6 +113,7 @@ extern "C" { #define DRM_XE_EXEC_QUEUE_SET_PROPERTY 0x0e #define DRM_XE_VM_GET_PROPERTY 0x0f #define DRM_XE_VM_RESTART 0x10 +#define DRM_XE_WATCH_QUEUE 0x11 =20 /* Must be kept compact -- no holes */ =20 @@ -132,6 +134,7 @@ extern "C" { #define DRM_IOCTL_XE_EXEC_QUEUE_SET_PROPERTY DRM_IOW(DRM_COMMAND_BASE + DR= M_XE_EXEC_QUEUE_SET_PROPERTY, struct drm_xe_exec_queue_set_property) #define DRM_IOCTL_XE_VM_GET_PROPERTY DRM_IOWR(DRM_COMMAND_BASE + DRM_XE_V= M_GET_PROPERTY, struct drm_xe_vm_get_property) #define DRM_IOCTL_XE_VM_RESTART DRM_IOW(DRM_COMMAND_BASE + DRM_XE_VM_RES= TART, struct drm_xe_vm_restart) +#define DRM_IOCTL_XE_WATCH_QUEUE DRM_IOW(DRM_COMMAND_BASE + DRM_XE_WATCH_= QUEUE, struct drm_xe_watch_queue) =20 /** * DOC: Xe IOCTL Extensions @@ -2653,6 +2656,48 @@ enum drm_xe_ras_error_component { [DRM_XE_RAS_ERR_COMP_SOC_INTERNAL] =3D "soc-internal" \ } =20 +/** + * 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 cre= ated + * through the same file) are delivered, preventing information leaks betw= een + * processes sharing the same GPU device. + * + * The pipe must first be opened with O_NOTIFICATION_PIPE (i.e. O_EXCL pas= sed + * 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 u= sed + * 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=E2=80=93255) embedded in the watch notificati= on + * 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/uapi/drm/xe_drm_events.h b/include/uapi/drm/xe_drm_eve= nts.h new file mode 100644 index 000000000000..6cc7528bfb9b --- /dev/null +++ b/include/uapi/drm/xe_drm_events.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright =C2=A9 2026 Intel Corporation + */ + +#ifndef _UAPI_XE_DRM_EVENTS_H_ +#define _UAPI_XE_DRM_EVENTS_H_ + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#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 fatal or resource error 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 =3D 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 encountered an error */ + __u32 vm_id; + + /** @error_code: error code describing the error condition (negative errn= o) */ + __s32 error_code; + + /** + * @timestamp_ns: CLOCK_MONOTONIC timestamp in nanoseconds at the + * point the error was detected + */ + __u64 timestamp_ns; +}; + +#if defined(__cplusplus) +} +#endif + +#endif /* _UAPI_XE_DRM_EVENTS_H_ */ --=20 2.54.0