diff options
| author | Niranjana Vishwanathapura <niranjana.vishwanathapura@intel.com> | 2026-07-13 13:23:18 -0700 |
|---|---|---|
| committer | Niranjana Vishwanathapura <niranjana.vishwanathapura@intel.com> | 2026-07-13 13:29:23 -0700 |
| commit | 412c885692cade356a394835934d3f36ae8f6aca (patch) | |
| tree | 1a401d27a418d09b98025ede2ea39a48c42d7b5a /drivers/gpu/drm/xe | |
| parent | dad2af2da9ead8a390153c0f042feacd5056888b (diff) | |
| download | linux-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.h | 9 | ||||
| -rw-r--r-- | drivers/gpu/drm/xe/xe_execlist.c | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/xe/xe_guc_submit.c | 39 |
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, }; |
