if (extensions) { /* * may set q->usm, must come before xe_lrc_create(), * may overwrite q->sched_props, must come before q->ops->init()
*/
err = exec_queue_user_extensions(xe, q, extensions, 0); if (err) {
__xe_exec_queue_free(q); return ERR_PTR(err);
}
}
return q;
}
staticint __xe_exec_queue_init(struct xe_exec_queue *q)
{ int i, err;
u32 flags = 0;
/* * PXP workloads executing on RCS or CCS must run in isolation (i.e. no * other workload can use the EUs at the same time). On MTL this is done * by setting the RUNALONE bit in the LRC, while starting on Xe2 there * is a dedicated bit for it.
*/ if (xe_exec_queue_uses_pxp(q) &&
(q->class == XE_ENGINE_CLASS_RENDER || q->class == XE_ENGINE_CLASS_COMPUTE)) { if (GRAPHICS_VER(gt_to_xe(q->gt)) >= 20)
flags |= XE_LRC_CREATE_PXP; else
flags |= XE_LRC_CREATE_RUNALONE;
}
for (i = 0; i < q->width; ++i) {
q->lrc[i] = xe_lrc_create(q->hwe, q->vm, SZ_16K, q->msix_vec, flags); if (IS_ERR(q->lrc[i])) {
err = PTR_ERR(q->lrc[i]); goto err_lrc;
}
}
err = q->ops->init(q); if (err) goto err_lrc;
return 0;
err_lrc: for (i = i - 1; i >= 0; --i)
xe_lrc_put(q->lrc[i]); return err;
}
staticvoid __xe_exec_queue_fini(struct xe_exec_queue *q)
{ int i;
q->ops->fini(q);
for (i = 0; i < q->width; ++i)
xe_lrc_put(q->lrc[i]);
}
/* VMs for GSCCS queues (and only those) must have the XE_VM_FLAG_GSC flag */
xe_assert(xe, !vm || (!!(vm->flags & XE_VM_FLAG_GSC) == !!(hwe->engine_id == XE_HW_ENGINE_GSCCS0)));
err = __xe_exec_queue_init(q); if (err) goto err_post_alloc;
/* * We can only add the queue to the PXP list after the init is complete, * because the PXP termination can call exec_queue_kill and that will * go bad if the queue is only half-initialized. This means that we * can't do it when we handle the PXP extension in __xe_exec_queue_alloc * and we need to do it here instead.
*/ if (xe_exec_queue_uses_pxp(q)) {
err = xe_pxp_exec_queue_add(xe->pxp, q); if (err) goto err_post_init;
}
void xe_exec_queue_fini(struct xe_exec_queue *q)
{ /* * Before releasing our ref to lrc and xef, accumulate our run ticks * and wakeup any waiters.
*/
xe_exec_queue_update_run_ticks(q); if (q->xef && atomic_dec_and_test(&q->xef->exec_queue.pending_removal))
wake_up_var(&q->xef->exec_queue.pending_removal);
if (xe_vm_in_preempt_fence_mode(vm)) {
q->lr.context = dma_fence_context_alloc(1);
err = xe_vm_add_compute_exec_queue(vm, q); if (XE_IOCTL_DBG(xe, err)) goto put_exec_queue;
}
if (q->vm && q->hwe->hw_engine_group) {
err = xe_hw_engine_group_add_exec_queue(q->hwe->hw_engine_group, q); if (err) goto put_exec_queue;
}
}
q->xef = xe_file_get(xef);
/* user id alloc must always be last in ioctl to prevent UAF */
err = xa_alloc(&xef->exec_queue.xa, &id, q, xa_limit_32b, GFP_KERNEL); if (err) goto kill_exec_queue;
/** * xe_exec_queue_ring_full() - Whether an exec_queue's ring is full * @q: The exec_queue * * Return: True if the exec_queue's ring is full, false otherwise.
*/ bool xe_exec_queue_ring_full(struct xe_exec_queue *q)
{ struct xe_lrc *lrc = q->lrc[0];
s32 max_job = lrc->ring.size / MAX_JOB_SIZE_BYTES;
/** * xe_exec_queue_is_idle() - Whether an exec_queue is idle. * @q: The exec_queue * * FIXME: Need to determine what to use as the short-lived * timeline lock for the exec_queues, so that the return value * of this function becomes more than just an advisory * snapshot in time. The timeline lock must protect the * seqno from racing submissions on the same exec_queue. * Typically vm->resv, but user-created timeline locks use the migrate vm * and never grabs the migrate vm->resv so we have a race there. * * Return: True if the exec_queue is idle, false otherwise.
*/ bool xe_exec_queue_is_idle(struct xe_exec_queue *q)
{ if (xe_exec_queue_is_parallel(q)) { int i;
for (i = 0; i < q->width; ++i) { if (xe_lrc_seqno(q->lrc[i]) !=
q->lrc[i]->fence_ctx.next_seqno - 1) returnfalse;
}
/** * xe_exec_queue_update_run_ticks() - Update run time in ticks for this exec queue * from hw * @q: The exec queue * * Update the timestamp saved by HW for this exec queue and save run ticks * calculated by using the delta from last update.
*/ void xe_exec_queue_update_run_ticks(struct xe_exec_queue *q)
{ struct xe_device *xe = gt_to_xe(q->gt); struct xe_lrc *lrc;
u64 old_ts, new_ts; int idx;
/* * Jobs that are executed by kernel doesn't have a corresponding xe_file * and thus are not accounted.
*/ if (!q->xef) return;
/* Synchronize with unbind while holding the xe file open */ if (!drm_dev_enter(&xe->drm, &idx)) return; /* * Only sample the first LRC. For parallel submission, all of them are * scheduled together and we compensate that below by multiplying by * width - this may introduce errors if that premise is not true and * they don't exit 100% aligned. On the other hand, looping through * the LRCs and reading them in different time could also introduce * errors.
*/
lrc = q->lrc[0];
new_ts = xe_lrc_update_timestamp(lrc, &old_ts);
q->xef->run_ticks[q->class] += (new_ts - old_ts) * q->width;
drm_dev_exit(idx);
}
/** * xe_exec_queue_kill - permanently stop all execution from an exec queue * @q: The exec queue * * This function permanently stops all activity on an exec queue. If the queue * is actively executing on the HW, it will be kicked off the engine; any * pending jobs are discarded and all future submissions are rejected. * This function is safe to call multiple times.
*/ void xe_exec_queue_kill(struct xe_exec_queue *q)
{ struct xe_exec_queue *eq = q, *next;
/** * xe_exec_queue_last_fence_put() - Drop ref to last fence * @q: The exec queue * @vm: The VM the engine does a bind or exec for
*/ void xe_exec_queue_last_fence_put(struct xe_exec_queue *q, struct xe_vm *vm)
{
xe_exec_queue_last_fence_lockdep_assert(q, vm);
xe_exec_queue_last_fence_put_unlocked(q);
}
/** * xe_exec_queue_last_fence_put_unlocked() - Drop ref to last fence unlocked * @q: The exec queue * * Only safe to be called from xe_exec_queue_destroy().
*/ void xe_exec_queue_last_fence_put_unlocked(struct xe_exec_queue *q)
{ if (q->last_fence) {
dma_fence_put(q->last_fence);
q->last_fence = NULL;
}
}
/** * xe_exec_queue_last_fence_get() - Get last fence * @q: The exec queue * @vm: The VM the engine does a bind or exec for * * Get last fence, takes a ref * * Returns: last fence if not signaled, dma fence stub if signaled
*/ struct dma_fence *xe_exec_queue_last_fence_get(struct xe_exec_queue *q, struct xe_vm *vm)
{ struct dma_fence *fence;
xe_exec_queue_last_fence_lockdep_assert(q, vm);
if (q->last_fence &&
test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &q->last_fence->flags))
xe_exec_queue_last_fence_put(q, vm);
/** * xe_exec_queue_last_fence_get_for_resume() - Get last fence * @q: The exec queue * @vm: The VM the engine does a bind or exec for * * Get last fence, takes a ref. Only safe to be called in the context of * resuming the hw engine group's long-running exec queue, when the group * semaphore is held. * * Returns: last fence if not signaled, dma fence stub if signaled
*/ struct dma_fence *xe_exec_queue_last_fence_get_for_resume(struct xe_exec_queue *q, struct xe_vm *vm)
{ struct dma_fence *fence;
/** * xe_exec_queue_last_fence_set() - Set last fence * @q: The exec queue * @vm: The VM the engine does a bind or exec for * @fence: The fence * * Set the last fence for the engine. Increases reference count for fence, when * closing engine xe_exec_queue_last_fence_put should be called.
*/ void xe_exec_queue_last_fence_set(struct xe_exec_queue *q, struct xe_vm *vm, struct dma_fence *fence)
{
xe_exec_queue_last_fence_lockdep_assert(q, vm);
/** * xe_exec_queue_last_fence_test_dep - Test last fence dependency of queue * @q: The exec queue * @vm: The VM the engine does a bind or exec for * * Returns: * -ETIME if there exists an unsignalled last fence dependency, zero otherwise.
*/ int xe_exec_queue_last_fence_test_dep(struct xe_exec_queue *q, struct xe_vm *vm)
{ struct dma_fence *fence; int err = 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.