summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/xe
diff options
context:
space:
mode:
authorNiranjana Vishwanathapura <niranjana.vishwanathapura@intel.com>2026-07-13 13:23:18 -0700
committerNiranjana Vishwanathapura <niranjana.vishwanathapura@intel.com>2026-07-13 13:29:23 -0700
commit412c885692cade356a394835934d3f36ae8f6aca (patch)
tree1a401d27a418d09b98025ede2ea39a48c42d7b5a /drivers/gpu/drm/xe
parentdad2af2da9ead8a390153c0f042feacd5056888b (diff)
downloadlinux-next-412c885692cade356a394835934d3f36ae8f6aca.tar.gz
linux-next-412c885692cade356a394835934d3f36ae8f6aca.zip
drm/xe/guc: add uninterruptible suspend wait for cross-process cleanup
Add a suspend_wait_blocking() exec queue op: an uninterruptible variant of suspend_wait() for callers that must complete a suspend on behalf of a queue that may belong to a different process than the calling task (e.g. cleanup/undo paths). An interruptible suspend_wait() returns -ERESTARTSYS when the calling task is signalled, which would leave the other process's queue suspended forever - a cross-process DoS. The blocking variant waits uninterruptibly and, on a genuine GuC timeout, bans and tears down the queue like suspend_wait() (shared via guc_exec_queue_suspend_timeout_ban()). It deliberately does not handle VF recovery since a blocking caller cannot retry. Assisted-by: Github-Copilot:Claude-opus-4.8 Signed-off-by: Niranjana Vishwanathapura <niranjana.vishwanathapura@intel.com> Reviewed-by: Matthew Brost <matthew.brost@intel.com> Link: https://patch.msgid.link/20260713202317.2187787-11-niranjana.vishwanathapura@intel.com
Diffstat (limited to 'drivers/gpu/drm/xe')
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue_types.h9
-rw-r--r--drivers/gpu/drm/xe/xe_execlist.c1
-rw-r--r--drivers/gpu/drm/xe/xe_guc_submit.c39
3 files changed, 49 insertions, 0 deletions
diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h
index dbb2ee8eb5de..0aa0823cbdaa 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue_types.h
+++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h
@@ -323,6 +323,15 @@ struct xe_exec_queue_ops {
*/
int (*suspend_wait)(struct xe_exec_queue *q);
/**
+ * @suspend_wait_blocking: Like @suspend_wait, but waits uninterruptibly
+ * (does not abort on the calling task's signals). For cleanup/undo paths
+ * that must complete a suspend on behalf of a queue that may belong to a
+ * different process than the caller: a signal to the caller must not
+ * abandon the wait, which would leave the other process's queue
+ * suspended forever (cross-process DoS). A timeout bans like suspend_wait.
+ */
+ int (*suspend_wait_blocking)(struct xe_exec_queue *q);
+ /**
* @resume: Resume exec queue execution, exec queue must be in a suspended
* state and dma fence returned from most recent suspend call must be
* signalled when this function is called.
diff --git a/drivers/gpu/drm/xe/xe_execlist.c b/drivers/gpu/drm/xe/xe_execlist.c
index 6b86b4f9cc1c..cc33ae80e8cf 100644
--- a/drivers/gpu/drm/xe/xe_execlist.c
+++ b/drivers/gpu/drm/xe/xe_execlist.c
@@ -468,6 +468,7 @@ static const struct xe_exec_queue_ops execlist_exec_queue_ops = {
.set_preempt_timeout = execlist_exec_queue_set_preempt_timeout,
.suspend = execlist_exec_queue_suspend,
.suspend_wait = execlist_exec_queue_suspend_wait,
+ .suspend_wait_blocking = execlist_exec_queue_suspend_wait,
.resume = execlist_exec_queue_resume,
.reset_status = execlist_exec_queue_reset_status,
};
diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c
index 3ece51451f86..de9c131fc62d 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit.c
+++ b/drivers/gpu/drm/xe/xe_guc_submit.c
@@ -2290,6 +2290,44 @@ retry:
return ret < 0 ? ret : 0;
}
+static int guc_exec_queue_suspend_wait_blocking(struct xe_exec_queue *q)
+{
+ struct xe_guc *guc = exec_queue_to_guc(q);
+ struct xe_device *xe = guc_to_xe(guc);
+ int ret;
+
+ /*
+ * Uninterruptible variant of guc_exec_queue_suspend_wait() for callers
+ * that must complete the wait on behalf of a queue possibly owned by a
+ * different process (e.g. cleanup/undo paths). An interruptible wait
+ * could return -ERESTARTSYS if the calling task is signalled, leaving
+ * that queue suspended forever (cross-process DoS).
+ *
+ * A timeout is still a real per-queue fault, so it bans and cleans up
+ * like suspend_wait(). VF recovery is deliberately not handled (no
+ * -EAGAIN) since a blocking caller cannot retry.
+ */
+ q = xe_exec_queue_multi_queue_primary(q);
+
+#define WAIT_COND \
+ (!READ_ONCE(q->guc->suspend_pending) || exec_queue_killed(q) || \
+ xe_guc_read_stopped(guc))
+
+ if (IS_SRIOV_VF(xe))
+ ret = wait_event_timeout(guc->ct.wq, WAIT_COND, HZ * 5);
+ else
+ ret = wait_event_timeout(q->guc->suspend_wait, WAIT_COND, HZ * 5);
+
+#undef WAIT_COND
+
+ if (!ret) {
+ guc_exec_queue_suspend_timeout_ban(q);
+ return -ETIME;
+ }
+
+ return 0;
+}
+
static void guc_exec_queue_resume(struct xe_exec_queue *q)
{
struct xe_gpu_scheduler *sched = &q->guc->sched;
@@ -2329,6 +2367,7 @@ static const struct xe_exec_queue_ops guc_exec_queue_ops = {
.set_multi_queue_priority = guc_exec_queue_set_multi_queue_priority,
.suspend = guc_exec_queue_suspend,
.suspend_wait = guc_exec_queue_suspend_wait,
+ .suspend_wait_blocking = guc_exec_queue_suspend_wait_blocking,
.resume = guc_exec_queue_resume,
.reset_status = guc_exec_queue_reset_status,
};