/* * Open coded one half of intel_context_enter, which we have to omit * here (see the large comment below) and because the other part must * not be called due constructing directly with __i915_request_create * which increments active count via intel_context_mark_active.
*/
GEM_BUG_ON(rq->context->active_count != 1);
__intel_gt_pm_get(engine->gt);
rq->context->wakeref = intel_wakeref_track(&engine->gt->wakeref);
/* * We have to serialise all potential retirement paths with our * submission, as we don't want to underflow either the * engine->wakeref.counter or our timeline->active_count. * * Equally, we cannot allow a new submission to start until * after we finish queueing, nor could we allow that submitter * to retire us before we are ready!
*/
spin_lock(&timelines->lock);
/* Let intel_gt_retire_requests() retire us (acquired under lock) */ if (!atomic_fetch_inc(&tl->active_count))
list_add_tail(&tl->link, &timelines->active_list);
/* Hand the request over to HW and so engine_retire() */
__i915_request_queue_bh(rq);
/* Let new submissions commence (and maybe retire this timeline) */
__intel_wakeref_defer_park(&engine->wakeref);
/* * This is execlist specific behaviour intended to ensure the GPU is * idle by switching to a known 'safe' context. With GuC submission, the * same idle guarantee is achieved by other means (disabling * scheduling). Further, switching to a 'safe' context has no effect * with GuC submission as the scheduler can just switch back again. * * FIXME: Move this backend scheduler specific behaviour into the * scheduler backend.
*/ if (intel_engine_uses_guc(engine)) returntrue;
/* GPU is pointing to the void, as good as in the kernel context. */ if (intel_gt_is_wedged(engine->gt)) returntrue;
/* Already inside the kernel context, safe to power down. */ if (engine->wakeref_serial == engine->serial) returntrue;
/* * Note, we do this without taking the timeline->mutex. We cannot * as we may be called while retiring the kernel context and so * already underneath the timeline->mutex. Instead we rely on the * exclusive property of the __engine_park that prevents anyone * else from creating a request on this engine. This also requires * that the ring is empty and we avoid any waits while constructing * the context, as they assume protection by the timeline->mutex. * This should hold true as we can only park the engine after * retiring the last request, thus all rings should be empty and * all timelines idle. * * For unlocking, there are 2 other parties and the GPU who have a * stake here. * * A new gpu user will be waiting on the engine-pm to start their * engine_unpark. New waiters are predicated on engine->wakeref.count * and so intel_wakeref_defer_park() acts like a mutex_unlock of the * engine->wakeref. * * The other party is intel_gt_retire_requests(), which is walking the * list of active timelines looking for completions. Meanwhile as soon * as we call __i915_request_queue(), the GPU may complete our request. * Ergo, if we put ourselves on the timelines.active_list * (se intel_timeline_enter()) before we increment the * engine->wakeref.count, we may see the request completion and retire * it causing an underflow of the engine->wakeref.
*/
set_bit(CONTEXT_IS_PARKING, &ce->flags);
GEM_BUG_ON(atomic_read(&ce->timeline->active_count) < 0);
rq = __i915_request_create(ce, GFP_NOWAIT); if (IS_ERR(rq)) /* Context switch failed, hope for the best! Maybe reset? */ goto out_unlock;
/* Check again on the next retirement. */
engine->wakeref_serial = engine->serial + 1;
i915_request_add_active_barriers(rq);
/* Install ourselves as a preemption barrier */
rq->sched.attr.priority = I915_PRIORITY_BARRIER; if (likely(!__i915_request_commit(rq))) { /* engine should be idle! */ /* * Use an interrupt for precise measurement of duration, * otherwise we rely on someone else retiring all the requests * which may delay the signaling (i.e. we will likely wait * until the background request retirement running every * second or two).
*/
BUILD_BUG_ON(sizeof(rq->duration) > sizeof(rq->submitq));
dma_fence_add_callback(&rq->fence, &rq->duration.cb, duration);
rq->duration.emitted = ktime_get();
}
/* Expose ourselves to the world */
__queue_and_release_pm(rq, ce->timeline, engine);
result = false;
out_unlock:
clear_bit(CONTEXT_IS_PARKING, &ce->flags); return result;
}
/* * If one and only one request is completed between pm events, * we know that we are inside the kernel context and it is * safe to power down. (We are paranoid in case that runtime * suspend causes corruption to the active context image, and * want to avoid that impacting userspace.)
*/ if (!switch_to_kernel_context(engine)) return -EBUSY;
ENGINE_TRACE(engine, "parked\n");
call_idle_barriers(engine); /* cleanup after wedging */
/** * intel_engine_reset_pinned_contexts - Reset the pinned contexts of * an engine. * @engine: The engine whose pinned contexts we want to reset. * * Typically the pinned context LMEM images lose or get their content * corrupted on suspend. This function resets their images.
*/ void intel_engine_reset_pinned_contexts(struct intel_engine_cs *engine)
{ struct intel_context *ce;
list_for_each_entry(ce, &engine->pinned_contexts_list,
pinned_contexts_link) { /* kernel context gets reset at __engine_unpark() */ if (ce == engine->kernel_context) continue;
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.