linux-stable/drivers/gpu/drm/i915/i915_scheduler_types.h

206 lines
6.1 KiB
C
Raw Normal View History

/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2018 Intel Corporation
*/
#ifndef _I915_SCHEDULER_TYPES_H_
#define _I915_SCHEDULER_TYPES_H_
#include <linux/list.h>
#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)
2019-04-01 16:26:41 +00:00
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;
drm/i915: Bump signaler priority on adding a waiter The handling of the no-preemption priority level imposes the restriction that we need to maintain the implied ordering even though preemption is disabled. Otherwise we may end up with an AB-BA deadlock across multiple engine due to a real preemption event reordering the no-preemption WAITs. To resolve this issue we currently promote all requests to WAIT on unsubmission, however this interferes with the timeslicing requirement that we do not apply any implicit promotion that will defeat the round-robin timeslice list. (If we automatically promote the active request it will go back to the head of the queue and not the tail!) So we need implicit promotion to prevent reordering around semaphores where we are not allowed to preempt, and we must avoid implicit promotion on unsubmission. So instead of at unsubmit, if we apply that implicit promotion on adding the dependency, we avoid the semaphore deadlock and we also reduce the gains made by the promotion for user space waiting. Furthermore, by keeping the earlier dependencies at a higher level, we reduce the search space for timeslicing without altering runtime scheduling too badly (no dependencies at all will be assigned a higher priority for rrul). v2: Limit the bump to external edges (as originally intended) i.e. between contexts and out to the user. Testcase: igt/gem_concurrent_blit Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20190515130052.4475-3-chris@chris-wilson.co.uk
2019-05-15 13:00:50 +00:00
#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_ */