mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 14:53:58 -04:00
clangd reports many "unused header" warnings throughout the Xe driver. Start working to clean this up by removing unnecessary includes in our .c files and/or replacing them with explicit includes of other headers that were previously being included indirectly. By far the most common offender here was unnecessary inclusion of xe_gt.h. That likely originates from the early days of xe.ko when xe_mmio did not exist and all register accesses, including those unrelated to GTs, were done with GT functions. There's still a lot of additional #include cleanup that can be done in the headers themselves; that will come as a followup series. v2: - Squash the 79-patch series down to a single patch. (MattB) Reviewed-by: Matthew Brost <matthew.brost@intel.com> Link: https://patch.msgid.link/20260115032803.4067824-2-matthew.d.roper@intel.com Signed-off-by: Matt Roper <matthew.d.roper@intel.com>
408 lines
9.5 KiB
C
408 lines
9.5 KiB
C
// SPDX-License-Identifier: MIT
|
|
/*
|
|
* Copyright © 2024 Intel Corporation
|
|
*/
|
|
|
|
#include <drm/drm_managed.h>
|
|
|
|
#include "xe_assert.h"
|
|
#include "xe_device_types.h"
|
|
#include "xe_exec_queue.h"
|
|
#include "xe_gt.h"
|
|
#include "xe_gt_stats.h"
|
|
#include "xe_hw_engine_group.h"
|
|
#include "xe_sync.h"
|
|
#include "xe_vm.h"
|
|
|
|
static void
|
|
hw_engine_group_resume_lr_jobs_func(struct work_struct *w)
|
|
{
|
|
struct xe_exec_queue *q;
|
|
struct xe_hw_engine_group *group = container_of(w, struct xe_hw_engine_group, resume_work);
|
|
int err;
|
|
enum xe_hw_engine_group_execution_mode previous_mode;
|
|
|
|
err = xe_hw_engine_group_get_mode(group, EXEC_MODE_LR, &previous_mode,
|
|
NULL, 0);
|
|
if (err)
|
|
return;
|
|
|
|
if (previous_mode == EXEC_MODE_LR)
|
|
goto put;
|
|
|
|
list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) {
|
|
if (!xe_vm_in_fault_mode(q->vm))
|
|
continue;
|
|
|
|
q->ops->resume(q);
|
|
}
|
|
|
|
put:
|
|
xe_hw_engine_group_put(group);
|
|
}
|
|
|
|
static struct xe_hw_engine_group *
|
|
hw_engine_group_alloc(struct xe_device *xe)
|
|
{
|
|
struct xe_hw_engine_group *group;
|
|
int err;
|
|
|
|
group = drmm_kzalloc(&xe->drm, sizeof(*group), GFP_KERNEL);
|
|
if (!group)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
group->resume_wq = alloc_workqueue("xe-resume-lr-jobs-wq", 0, 0);
|
|
if (!group->resume_wq)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
err = drmm_add_action_or_reset(&xe->drm, __drmm_workqueue_release, group->resume_wq);
|
|
if (err)
|
|
return ERR_PTR(err);
|
|
|
|
init_rwsem(&group->mode_sem);
|
|
INIT_WORK(&group->resume_work, hw_engine_group_resume_lr_jobs_func);
|
|
INIT_LIST_HEAD(&group->exec_queue_list);
|
|
|
|
return group;
|
|
}
|
|
|
|
/**
|
|
* xe_hw_engine_setup_groups() - Setup the hw engine groups for the gt
|
|
* @gt: The gt for which groups are setup
|
|
*
|
|
* Return: 0 on success, negative error code on error.
|
|
*/
|
|
int xe_hw_engine_setup_groups(struct xe_gt *gt)
|
|
{
|
|
struct xe_hw_engine *hwe;
|
|
enum xe_hw_engine_id id;
|
|
struct xe_hw_engine_group *group_rcs_ccs, *group_bcs, *group_vcs_vecs;
|
|
struct xe_device *xe = gt_to_xe(gt);
|
|
|
|
group_rcs_ccs = hw_engine_group_alloc(xe);
|
|
if (IS_ERR(group_rcs_ccs))
|
|
return PTR_ERR(group_rcs_ccs);
|
|
|
|
group_bcs = hw_engine_group_alloc(xe);
|
|
if (IS_ERR(group_bcs))
|
|
return PTR_ERR(group_bcs);
|
|
|
|
group_vcs_vecs = hw_engine_group_alloc(xe);
|
|
if (IS_ERR(group_vcs_vecs))
|
|
return PTR_ERR(group_vcs_vecs);
|
|
|
|
for_each_hw_engine(hwe, gt, id) {
|
|
switch (hwe->class) {
|
|
case XE_ENGINE_CLASS_COPY:
|
|
hwe->hw_engine_group = group_bcs;
|
|
break;
|
|
case XE_ENGINE_CLASS_RENDER:
|
|
case XE_ENGINE_CLASS_COMPUTE:
|
|
hwe->hw_engine_group = group_rcs_ccs;
|
|
break;
|
|
case XE_ENGINE_CLASS_VIDEO_DECODE:
|
|
case XE_ENGINE_CLASS_VIDEO_ENHANCE:
|
|
hwe->hw_engine_group = group_vcs_vecs;
|
|
break;
|
|
case XE_ENGINE_CLASS_OTHER:
|
|
break;
|
|
case XE_ENGINE_CLASS_MAX:
|
|
xe_gt_assert(gt, false);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* xe_hw_engine_group_add_exec_queue() - Add an exec queue to a hw engine group
|
|
* @group: The hw engine group
|
|
* @q: The exec_queue
|
|
*
|
|
* Return: 0 on success,
|
|
* -EINTR if the lock could not be acquired
|
|
*/
|
|
int xe_hw_engine_group_add_exec_queue(struct xe_hw_engine_group *group, struct xe_exec_queue *q)
|
|
{
|
|
int err;
|
|
struct xe_device *xe = gt_to_xe(q->gt);
|
|
|
|
xe_assert(xe, group);
|
|
xe_assert(xe, !(q->flags & EXEC_QUEUE_FLAG_VM));
|
|
xe_assert(xe, q->vm);
|
|
|
|
if (xe_vm_in_preempt_fence_mode(q->vm))
|
|
return 0;
|
|
|
|
err = down_write_killable(&group->mode_sem);
|
|
if (err)
|
|
return err;
|
|
|
|
if (xe_vm_in_fault_mode(q->vm) && group->cur_mode == EXEC_MODE_DMA_FENCE) {
|
|
q->ops->suspend(q);
|
|
err = q->ops->suspend_wait(q);
|
|
if (err)
|
|
goto err_suspend;
|
|
|
|
xe_hw_engine_group_resume_faulting_lr_jobs(group);
|
|
}
|
|
|
|
list_add(&q->hw_engine_group_link, &group->exec_queue_list);
|
|
up_write(&group->mode_sem);
|
|
|
|
return 0;
|
|
|
|
err_suspend:
|
|
up_write(&group->mode_sem);
|
|
return err;
|
|
}
|
|
ALLOW_ERROR_INJECTION(xe_hw_engine_group_add_exec_queue, ERRNO);
|
|
|
|
/**
|
|
* xe_hw_engine_group_del_exec_queue() - Delete an exec queue from a hw engine group
|
|
* @group: The hw engine group
|
|
* @q: The exec_queue
|
|
*/
|
|
void xe_hw_engine_group_del_exec_queue(struct xe_hw_engine_group *group, struct xe_exec_queue *q)
|
|
{
|
|
struct xe_device *xe = gt_to_xe(q->gt);
|
|
|
|
xe_assert(xe, group);
|
|
xe_assert(xe, q->vm);
|
|
|
|
down_write(&group->mode_sem);
|
|
|
|
if (!list_empty(&q->hw_engine_group_link))
|
|
list_del(&q->hw_engine_group_link);
|
|
|
|
up_write(&group->mode_sem);
|
|
}
|
|
|
|
/**
|
|
* xe_hw_engine_group_resume_faulting_lr_jobs() - Asynchronously resume the hw engine group's
|
|
* faulting LR jobs
|
|
* @group: The hw engine group
|
|
*/
|
|
void xe_hw_engine_group_resume_faulting_lr_jobs(struct xe_hw_engine_group *group)
|
|
{
|
|
queue_work(group->resume_wq, &group->resume_work);
|
|
}
|
|
|
|
/**
|
|
* xe_hw_engine_group_suspend_faulting_lr_jobs() - Suspend the faulting LR jobs of this group
|
|
* @group: The hw engine group
|
|
* @has_deps: dma-fence job triggering suspend has dependencies
|
|
*
|
|
* Return: 0 on success, negative error code on error.
|
|
*/
|
|
static int xe_hw_engine_group_suspend_faulting_lr_jobs(struct xe_hw_engine_group *group,
|
|
bool has_deps)
|
|
{
|
|
int err;
|
|
struct xe_exec_queue *q;
|
|
struct xe_gt *gt = NULL;
|
|
bool need_resume = false;
|
|
ktime_t start = xe_gt_stats_ktime_get();
|
|
|
|
lockdep_assert_held_write(&group->mode_sem);
|
|
|
|
list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) {
|
|
bool idle_skip_suspend;
|
|
|
|
if (!xe_vm_in_fault_mode(q->vm))
|
|
continue;
|
|
|
|
idle_skip_suspend = xe_exec_queue_idle_skip_suspend(q);
|
|
if (!idle_skip_suspend && has_deps)
|
|
return -EAGAIN;
|
|
|
|
xe_gt_stats_incr(q->gt, XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_COUNT, 1);
|
|
if (idle_skip_suspend)
|
|
xe_gt_stats_incr(q->gt,
|
|
XE_GT_STATS_ID_HW_ENGINE_GROUP_SKIP_LR_QUEUE_COUNT, 1);
|
|
|
|
need_resume |= !idle_skip_suspend;
|
|
q->ops->suspend(q);
|
|
gt = q->gt;
|
|
}
|
|
|
|
list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) {
|
|
if (!xe_vm_in_fault_mode(q->vm))
|
|
continue;
|
|
|
|
err = q->ops->suspend_wait(q);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
if (gt) {
|
|
xe_gt_stats_incr(gt,
|
|
XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_US,
|
|
xe_gt_stats_ktime_us_delta(start));
|
|
}
|
|
|
|
if (need_resume)
|
|
xe_hw_engine_group_resume_faulting_lr_jobs(group);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* xe_hw_engine_group_wait_for_dma_fence_jobs() - Wait for dma fence jobs to complete
|
|
* @group: The hw engine group
|
|
*
|
|
* This function is not meant to be called directly from a user IOCTL as dma_fence_wait()
|
|
* is not interruptible.
|
|
*
|
|
* Return: 0 on success,
|
|
* -ETIME if waiting for one job failed
|
|
*/
|
|
static int xe_hw_engine_group_wait_for_dma_fence_jobs(struct xe_hw_engine_group *group)
|
|
{
|
|
long timeout;
|
|
struct xe_exec_queue *q;
|
|
struct xe_gt *gt = NULL;
|
|
struct dma_fence *fence;
|
|
ktime_t start = xe_gt_stats_ktime_get();
|
|
|
|
lockdep_assert_held_write(&group->mode_sem);
|
|
|
|
list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) {
|
|
if (xe_vm_in_lr_mode(q->vm))
|
|
continue;
|
|
|
|
xe_gt_stats_incr(q->gt, XE_GT_STATS_ID_HW_ENGINE_GROUP_WAIT_DMA_QUEUE_COUNT, 1);
|
|
fence = xe_exec_queue_last_fence_get_for_resume(q, q->vm);
|
|
timeout = dma_fence_wait(fence, false);
|
|
dma_fence_put(fence);
|
|
gt = q->gt;
|
|
|
|
if (timeout < 0)
|
|
return -ETIME;
|
|
}
|
|
|
|
if (gt) {
|
|
xe_gt_stats_incr(gt,
|
|
XE_GT_STATS_ID_HW_ENGINE_GROUP_WAIT_DMA_QUEUE_US,
|
|
xe_gt_stats_ktime_us_delta(start));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int switch_mode(struct xe_hw_engine_group *group, bool has_deps)
|
|
{
|
|
int err = 0;
|
|
enum xe_hw_engine_group_execution_mode new_mode;
|
|
|
|
lockdep_assert_held_write(&group->mode_sem);
|
|
|
|
switch (group->cur_mode) {
|
|
case EXEC_MODE_LR:
|
|
new_mode = EXEC_MODE_DMA_FENCE;
|
|
err = xe_hw_engine_group_suspend_faulting_lr_jobs(group,
|
|
has_deps);
|
|
break;
|
|
case EXEC_MODE_DMA_FENCE:
|
|
new_mode = EXEC_MODE_LR;
|
|
err = xe_hw_engine_group_wait_for_dma_fence_jobs(group);
|
|
break;
|
|
}
|
|
|
|
if (err)
|
|
return err;
|
|
|
|
group->cur_mode = new_mode;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int wait_syncs(struct xe_sync_entry *syncs, int num_syncs)
|
|
{
|
|
int err, i;
|
|
|
|
for (i = 0; i < num_syncs; ++i) {
|
|
err = xe_sync_entry_wait(syncs + i);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* xe_hw_engine_group_get_mode() - Get the group to execute in the new mode
|
|
* @group: The hw engine group
|
|
* @new_mode: The new execution mode
|
|
* @previous_mode: Pointer to the previous mode provided for use by caller
|
|
* @syncs: Syncs from exec IOCTL
|
|
* @num_syncs: Number of syncs from exec IOCTL
|
|
*
|
|
* Return: 0 if successful, -EINTR if locking failed.
|
|
*/
|
|
int xe_hw_engine_group_get_mode(struct xe_hw_engine_group *group,
|
|
enum xe_hw_engine_group_execution_mode new_mode,
|
|
enum xe_hw_engine_group_execution_mode *previous_mode,
|
|
struct xe_sync_entry *syncs, int num_syncs)
|
|
__acquires(&group->mode_sem)
|
|
{
|
|
bool has_deps = !!num_syncs;
|
|
int err = down_read_interruptible(&group->mode_sem);
|
|
|
|
if (err)
|
|
return err;
|
|
|
|
*previous_mode = group->cur_mode;
|
|
|
|
if (new_mode != group->cur_mode) {
|
|
up_read(&group->mode_sem);
|
|
retry:
|
|
err = down_write_killable(&group->mode_sem);
|
|
if (err)
|
|
return err;
|
|
|
|
if (new_mode != group->cur_mode) {
|
|
err = switch_mode(group, has_deps);
|
|
if (err) {
|
|
up_write(&group->mode_sem);
|
|
|
|
if (err != -EAGAIN)
|
|
return err;
|
|
|
|
err = wait_syncs(syncs, num_syncs);
|
|
if (err)
|
|
return err;
|
|
|
|
has_deps = false;
|
|
goto retry;
|
|
}
|
|
}
|
|
downgrade_write(&group->mode_sem);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* xe_hw_engine_group_put() - Put the group
|
|
* @group: The hw engine group
|
|
*/
|
|
void xe_hw_engine_group_put(struct xe_hw_engine_group *group)
|
|
__releases(&group->mode_sem)
|
|
{
|
|
up_read(&group->mode_sem);
|
|
}
|
|
|
|
/**
|
|
* xe_hw_engine_group_find_exec_mode() - Find the execution mode for this exec queue
|
|
* @q: The exec_queue
|
|
*/
|
|
enum xe_hw_engine_group_execution_mode
|
|
xe_hw_engine_group_find_exec_mode(struct xe_exec_queue *q)
|
|
{
|
|
if (xe_vm_in_fault_mode(q->vm))
|
|
return EXEC_MODE_LR;
|
|
else
|
|
return EXEC_MODE_DMA_FENCE;
|
|
}
|