diff options
| -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, }; |
