/* * SPDX-License-Identifier: MIT * * Copyright © 2018 Intel Corporation */ #ifndef _I915_SCHEDULER_TYPES_H_ #define _I915_SCHEDULER_TYPES_H_ #include #include "gt/intel_engine_types.h" #include "i915_priolist_types.h" struct drm_i915_private; struct i915_request; struct intel_engine_cs; struct i915_sched_attr { /** * @priority: execution and service priority * * All clients are equal, but some are more equal than others! * * Requests from a context with a greater (more positive) value of * @priority will be executed before those with a lower @priority * value, forming a simple QoS. * * The &drm_i915_private.kernel_context is assigned the lowest priority. */ int priority; }; /* * "People assume that time is a strict progression of cause to effect, but * actually, from a nonlinear, non-subjective viewpoint, it's more like a big * ball of wibbly-wobbly, timey-wimey ... stuff." -The Doctor, 2015 * * Requests exist in a complex web of interdependencies. Each request * has to wait for some other request to complete before it is ready to be run * (e.g. we have to wait until the pixels have been rendering into a texture * before we can copy from it). We track the readiness of a request in terms * of fences, but we also need to keep the dependency tree for the lifetime * of the request (beyond the life of an individual fence). We use the tree * at various points to reorder the requests whilst keeping the requests * in order with respect to their various dependencies. * * There is no active component to the "scheduler". As we know the dependency * DAG of each request, we are able to insert it into a sorted queue when it * is ready, and are able to reorder its portion of the graph to accommodate * dynamic priority changes. * * Ok, there is now one active element to the "scheduler" in the backends. * We let a new context run for a small amount of time before re-evaluating * the run order. As we re-evaluate, we maintain the strict ordering of * dependencies, but attempt to rotate the active contexts (the current context * is put to the back of its priority queue, then reshuffling its dependents). * This provides minimal timeslicing and prevents a userspace hog (e.g. * something waiting on a user semaphore [VkEvent]) from denying service to * others. */ struct i915_sched_node { struct list_head signalers_list; /* those before us, we depend upon */ struct list_head waiters_list; /* those after us, they depend upon us */ struct list_head link; struct i915_sched_attr attr; unsigned int flags; #define I915_SCHED_HAS_EXTERNAL_CHAIN BIT(0) intel_engine_mask_t semaphores; }; struct i915_dependency { struct i915_sched_node *signaler; struct i915_sched_node *waiter; struct list_head signal_link; struct list_head wait_link; struct list_head dfs_link; unsigned long flags; #define I915_DEPENDENCY_ALLOC BIT(0) #define I915_DEPENDENCY_EXTERNAL BIT(1) #define I915_DEPENDENCY_WEAK BIT(2) }; #define for_each_waiter(p__, rq__) \ list_for_each_entry_lockless(p__, \ &(rq__)->sched.waiters_list, \ wait_link) #define for_each_signaler(p__, rq__) \ list_for_each_entry_rcu(p__, \ &(rq__)->sched.signalers_list, \ signal_link) /** * struct i915_sched_engine - scheduler engine * * A schedule engine represents a submission queue with different priority * bands. It contains all the common state (relative to the backend) to queue, * track, and submit a request. * * This object at the moment is quite i915 specific but will transition into a * container for the drm_gpu_scheduler plus a few other variables once the i915 * is integrated with the DRM scheduler. */ struct i915_sched_engine { /** * @ref: reference count of schedule engine object */ struct kref ref; /** * @lock: protects requests in priority lists, requests, hold and * tasklet while running */ spinlock_t lock; /** * @requests: list of requests inflight on this schedule engine */ struct list_head requests; /** * @hold: list of ready requests, but on hold */ struct list_head hold; /** * @tasklet: softirq tasklet for submission */ struct tasklet_struct tasklet; /** * @default_priolist: priority list for I915_PRIORITY_NORMAL */ struct i915_priolist default_priolist; /** * @queue_priority_hint: Highest pending priority. * * When we add requests into the queue, or adjust the priority of * executing requests, we compute the maximum priority of those * pending requests. We can then use this value to determine if * we need to preempt the executing requests to service the queue. * However, since the we may have recorded the priority of an inflight * request we wanted to preempt but since completed, at the time of * dequeuing the priority hint may no longer may match the highest * available request priority. */ int queue_priority_hint; /** * @queue: queue of requests, in priority lists */ struct rb_root_cached queue; /** * @no_priolist: priority lists disabled */ bool no_priolist; /** * @private_data: private data of the submission backend */ void *private_data; /** * @destroy: destroy schedule engine / cleanup in backend */ void (*destroy)(struct kref *kref); /** * @disabled: check if backend has disabled submission */ bool (*disabled)(struct i915_sched_engine *sched_engine); /** * @kick_backend: kick backend after a request's priority has changed */ void (*kick_backend)(const struct i915_request *rq, int prio); /** * @bump_inflight_request_prio: update priority of an inflight request */ void (*bump_inflight_request_prio)(struct i915_request *rq, int prio); /** * @retire_inflight_request_prio: indicate request is retired to * priority tracking */ void (*retire_inflight_request_prio)(struct i915_request *rq); /** * @schedule: adjust priority of request * * Call when the priority on a request has changed and it and its * dependencies may need rescheduling. Note the request itself may * not be ready to run! */ void (*schedule)(struct i915_request *request, const struct i915_sched_attr *attr); }; #endif /* _I915_SCHEDULER_TYPES_H_ */