xref: /openbsd/sys/dev/pci/drm/i915/i915_scheduler.c (revision ebc65071)
1c349dbc7Sjsg /*
2c349dbc7Sjsg  * SPDX-License-Identifier: MIT
3c349dbc7Sjsg  *
4c349dbc7Sjsg  * Copyright © 2018 Intel Corporation
5c349dbc7Sjsg  */
6c349dbc7Sjsg 
7c349dbc7Sjsg #include <linux/mutex.h>
8c349dbc7Sjsg 
9c349dbc7Sjsg #include "i915_drv.h"
10c349dbc7Sjsg #include "i915_request.h"
11c349dbc7Sjsg #include "i915_scheduler.h"
12c349dbc7Sjsg 
135ca02815Sjsg static struct pool slab_dependencies;
145ca02815Sjsg static struct pool slab_priorities;
15c349dbc7Sjsg 
16c349dbc7Sjsg static DEFINE_SPINLOCK(schedule_lock);
17c349dbc7Sjsg 
18c349dbc7Sjsg static const struct i915_request *
node_to_request(const struct i915_sched_node * node)19c349dbc7Sjsg node_to_request(const struct i915_sched_node *node)
20c349dbc7Sjsg {
21c349dbc7Sjsg 	return container_of(node, const struct i915_request, sched);
22c349dbc7Sjsg }
23c349dbc7Sjsg 
node_started(const struct i915_sched_node * node)24c349dbc7Sjsg static inline bool node_started(const struct i915_sched_node *node)
25c349dbc7Sjsg {
26c349dbc7Sjsg 	return i915_request_started(node_to_request(node));
27c349dbc7Sjsg }
28c349dbc7Sjsg 
node_signaled(const struct i915_sched_node * node)29c349dbc7Sjsg static inline bool node_signaled(const struct i915_sched_node *node)
30c349dbc7Sjsg {
31c349dbc7Sjsg 	return i915_request_completed(node_to_request(node));
32c349dbc7Sjsg }
33c349dbc7Sjsg 
to_priolist(struct rb_node * rb)34c349dbc7Sjsg static inline struct i915_priolist *to_priolist(struct rb_node *rb)
35c349dbc7Sjsg {
36c349dbc7Sjsg 	return rb_entry(rb, struct i915_priolist, node);
37c349dbc7Sjsg }
38c349dbc7Sjsg 
assert_priolists(struct i915_sched_engine * const sched_engine)395ca02815Sjsg static void assert_priolists(struct i915_sched_engine * const sched_engine)
40c349dbc7Sjsg {
41c349dbc7Sjsg 	struct rb_node *rb;
425ca02815Sjsg 	long last_prio;
43c349dbc7Sjsg 
44c349dbc7Sjsg 	if (!IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM))
45c349dbc7Sjsg 		return;
46c349dbc7Sjsg 
475ca02815Sjsg 	GEM_BUG_ON(rb_first_cached(&sched_engine->queue) !=
485ca02815Sjsg 		   rb_first(&sched_engine->queue.rb_root));
49c349dbc7Sjsg 
50ad8b1aafSjsg 	last_prio = INT_MAX;
515ca02815Sjsg 	for (rb = rb_first_cached(&sched_engine->queue); rb; rb = rb_next(rb)) {
52c349dbc7Sjsg 		const struct i915_priolist *p = to_priolist(rb);
53c349dbc7Sjsg 
54ad8b1aafSjsg 		GEM_BUG_ON(p->priority > last_prio);
55c349dbc7Sjsg 		last_prio = p->priority;
56c349dbc7Sjsg 	}
57c349dbc7Sjsg }
58c349dbc7Sjsg 
59c349dbc7Sjsg struct list_head *
i915_sched_lookup_priolist(struct i915_sched_engine * sched_engine,int prio)605ca02815Sjsg i915_sched_lookup_priolist(struct i915_sched_engine *sched_engine, int prio)
61c349dbc7Sjsg {
62c349dbc7Sjsg 	struct i915_priolist *p;
63c349dbc7Sjsg 	struct rb_node **parent, *rb;
64c349dbc7Sjsg 	bool first = true;
65c349dbc7Sjsg 
665ca02815Sjsg 	lockdep_assert_held(&sched_engine->lock);
675ca02815Sjsg 	assert_priolists(sched_engine);
68c349dbc7Sjsg 
695ca02815Sjsg 	if (unlikely(sched_engine->no_priolist))
70c349dbc7Sjsg 		prio = I915_PRIORITY_NORMAL;
71c349dbc7Sjsg 
72c349dbc7Sjsg find_priolist:
73c349dbc7Sjsg 	/* most positive priority is scheduled first, equal priorities fifo */
74c349dbc7Sjsg 	rb = NULL;
755ca02815Sjsg 	parent = &sched_engine->queue.rb_root.rb_node;
76c349dbc7Sjsg 	while (*parent) {
77c349dbc7Sjsg 		rb = *parent;
78c349dbc7Sjsg 		p = to_priolist(rb);
79c349dbc7Sjsg 		if (prio > p->priority) {
80c349dbc7Sjsg 			parent = &rb->rb_left;
81c349dbc7Sjsg 		} else if (prio < p->priority) {
82c349dbc7Sjsg 			parent = &rb->rb_right;
83c349dbc7Sjsg 			first = false;
84c349dbc7Sjsg 		} else {
855ca02815Sjsg 			return &p->requests;
86c349dbc7Sjsg 		}
87c349dbc7Sjsg 	}
88c349dbc7Sjsg 
89c349dbc7Sjsg 	if (prio == I915_PRIORITY_NORMAL) {
905ca02815Sjsg 		p = &sched_engine->default_priolist;
91c349dbc7Sjsg 	} else {
92c349dbc7Sjsg #ifdef __linux__
935ca02815Sjsg 		p = kmem_cache_alloc(slab_priorities, GFP_ATOMIC);
94c349dbc7Sjsg #else
955ca02815Sjsg 		p = pool_get(&slab_priorities, PR_NOWAIT);
96c349dbc7Sjsg #endif
97c349dbc7Sjsg 		/* Convert an allocation failure to a priority bump */
98c349dbc7Sjsg 		if (unlikely(!p)) {
99c349dbc7Sjsg 			prio = I915_PRIORITY_NORMAL; /* recurses just once */
100c349dbc7Sjsg 
101c349dbc7Sjsg 			/* To maintain ordering with all rendering, after an
102c349dbc7Sjsg 			 * allocation failure we have to disable all scheduling.
103c349dbc7Sjsg 			 * Requests will then be executed in fifo, and schedule
104c349dbc7Sjsg 			 * will ensure that dependencies are emitted in fifo.
105c349dbc7Sjsg 			 * There will be still some reordering with existing
106c349dbc7Sjsg 			 * requests, so if userspace lied about their
107c349dbc7Sjsg 			 * dependencies that reordering may be visible.
108c349dbc7Sjsg 			 */
1095ca02815Sjsg 			sched_engine->no_priolist = true;
110c349dbc7Sjsg 			goto find_priolist;
111c349dbc7Sjsg 		}
112c349dbc7Sjsg 	}
113c349dbc7Sjsg 
114c349dbc7Sjsg 	p->priority = prio;
1155ca02815Sjsg 	INIT_LIST_HEAD(&p->requests);
116c349dbc7Sjsg 
1175ca02815Sjsg 	rb_link_node(&p->node, rb, parent);
1185ca02815Sjsg 	rb_insert_color_cached(&p->node, &sched_engine->queue, first);
1195ca02815Sjsg 
1205ca02815Sjsg 	return &p->requests;
121c349dbc7Sjsg }
122c349dbc7Sjsg 
__i915_priolist_free(struct i915_priolist * p)123c349dbc7Sjsg void __i915_priolist_free(struct i915_priolist *p)
124c349dbc7Sjsg {
125c349dbc7Sjsg #ifdef __linux__
1265ca02815Sjsg 	kmem_cache_free(slab_priorities, p);
127c349dbc7Sjsg #else
1285ca02815Sjsg 	pool_put(&slab_priorities, p);
129c349dbc7Sjsg #endif
130c349dbc7Sjsg }
131c349dbc7Sjsg 
132c349dbc7Sjsg struct sched_cache {
133c349dbc7Sjsg 	struct list_head *priolist;
134c349dbc7Sjsg };
135c349dbc7Sjsg 
1365ca02815Sjsg static struct i915_sched_engine *
lock_sched_engine(struct i915_sched_node * node,struct i915_sched_engine * locked,struct sched_cache * cache)1375ca02815Sjsg lock_sched_engine(struct i915_sched_node *node,
1385ca02815Sjsg 		  struct i915_sched_engine *locked,
139c349dbc7Sjsg 		  struct sched_cache *cache)
140c349dbc7Sjsg {
141c349dbc7Sjsg 	const struct i915_request *rq = node_to_request(node);
1425ca02815Sjsg 	struct i915_sched_engine *sched_engine;
143c349dbc7Sjsg 
144c349dbc7Sjsg 	GEM_BUG_ON(!locked);
145c349dbc7Sjsg 
146c349dbc7Sjsg 	/*
147c349dbc7Sjsg 	 * Virtual engines complicate acquiring the engine timeline lock,
148c349dbc7Sjsg 	 * as their rq->engine pointer is not stable until under that
149c349dbc7Sjsg 	 * engine lock. The simple ploy we use is to take the lock then
150c349dbc7Sjsg 	 * check that the rq still belongs to the newly locked engine.
151c349dbc7Sjsg 	 */
1525ca02815Sjsg 	while (locked != (sched_engine = READ_ONCE(rq->engine)->sched_engine)) {
1535ca02815Sjsg 		spin_unlock(&locked->lock);
154c349dbc7Sjsg 		memset(cache, 0, sizeof(*cache));
1555ca02815Sjsg 		spin_lock(&sched_engine->lock);
1565ca02815Sjsg 		locked = sched_engine;
157c349dbc7Sjsg 	}
158c349dbc7Sjsg 
1595ca02815Sjsg 	GEM_BUG_ON(locked != sched_engine);
160c349dbc7Sjsg 	return locked;
161c349dbc7Sjsg }
162c349dbc7Sjsg 
__i915_schedule(struct i915_sched_node * node,const struct i915_sched_attr * attr)163c349dbc7Sjsg static void __i915_schedule(struct i915_sched_node *node,
164c349dbc7Sjsg 			    const struct i915_sched_attr *attr)
165c349dbc7Sjsg {
166c349dbc7Sjsg 	const int prio = max(attr->priority, node->attr.priority);
1675ca02815Sjsg 	struct i915_sched_engine *sched_engine;
168c349dbc7Sjsg 	struct i915_dependency *dep, *p;
169c349dbc7Sjsg 	struct i915_dependency stack;
170c349dbc7Sjsg 	struct sched_cache cache;
171c349dbc7Sjsg 	DRM_LIST_HEAD(dfs);
172c349dbc7Sjsg 
173c349dbc7Sjsg 	/* Needed in order to use the temporary link inside i915_dependency */
174c349dbc7Sjsg 	lockdep_assert_held(&schedule_lock);
175c349dbc7Sjsg 	GEM_BUG_ON(prio == I915_PRIORITY_INVALID);
176c349dbc7Sjsg 
177c349dbc7Sjsg 	if (node_signaled(node))
178c349dbc7Sjsg 		return;
179c349dbc7Sjsg 
180c349dbc7Sjsg 	stack.signaler = node;
181c349dbc7Sjsg 	list_add(&stack.dfs_link, &dfs);
182c349dbc7Sjsg 
183c349dbc7Sjsg 	/*
184c349dbc7Sjsg 	 * Recursively bump all dependent priorities to match the new request.
185c349dbc7Sjsg 	 *
186c349dbc7Sjsg 	 * A naive approach would be to use recursion:
187c349dbc7Sjsg 	 * static void update_priorities(struct i915_sched_node *node, prio) {
188c349dbc7Sjsg 	 *	list_for_each_entry(dep, &node->signalers_list, signal_link)
189c349dbc7Sjsg 	 *		update_priorities(dep->signal, prio)
190c349dbc7Sjsg 	 *	queue_request(node);
191c349dbc7Sjsg 	 * }
192c349dbc7Sjsg 	 * but that may have unlimited recursion depth and so runs a very
193c349dbc7Sjsg 	 * real risk of overunning the kernel stack. Instead, we build
194c349dbc7Sjsg 	 * a flat list of all dependencies starting with the current request.
195c349dbc7Sjsg 	 * As we walk the list of dependencies, we add all of its dependencies
196c349dbc7Sjsg 	 * to the end of the list (this may include an already visited
197c349dbc7Sjsg 	 * request) and continue to walk onwards onto the new dependencies. The
198c349dbc7Sjsg 	 * end result is a topological list of requests in reverse order, the
199c349dbc7Sjsg 	 * last element in the list is the request we must execute first.
200c349dbc7Sjsg 	 */
201c349dbc7Sjsg 	list_for_each_entry(dep, &dfs, dfs_link) {
202c349dbc7Sjsg 		struct i915_sched_node *node = dep->signaler;
203c349dbc7Sjsg 
204c349dbc7Sjsg 		/* If we are already flying, we know we have no signalers */
205c349dbc7Sjsg 		if (node_started(node))
206c349dbc7Sjsg 			continue;
207c349dbc7Sjsg 
208c349dbc7Sjsg 		/*
209c349dbc7Sjsg 		 * Within an engine, there can be no cycle, but we may
210c349dbc7Sjsg 		 * refer to the same dependency chain multiple times
211c349dbc7Sjsg 		 * (redundant dependencies are not eliminated) and across
212c349dbc7Sjsg 		 * engines.
213c349dbc7Sjsg 		 */
214c349dbc7Sjsg 		list_for_each_entry(p, &node->signalers_list, signal_link) {
215c349dbc7Sjsg 			GEM_BUG_ON(p == dep); /* no cycles! */
216c349dbc7Sjsg 
217c349dbc7Sjsg 			if (node_signaled(p->signaler))
218c349dbc7Sjsg 				continue;
219c349dbc7Sjsg 
220c349dbc7Sjsg 			if (prio > READ_ONCE(p->signaler->attr.priority))
221c349dbc7Sjsg 				list_move_tail(&p->dfs_link, &dfs);
222c349dbc7Sjsg 		}
223c349dbc7Sjsg 	}
224c349dbc7Sjsg 
225c349dbc7Sjsg 	/*
226c349dbc7Sjsg 	 * If we didn't need to bump any existing priorities, and we haven't
227c349dbc7Sjsg 	 * yet submitted this request (i.e. there is no potential race with
228c349dbc7Sjsg 	 * execlists_submit_request()), we can set our own priority and skip
229c349dbc7Sjsg 	 * acquiring the engine locks.
230c349dbc7Sjsg 	 */
231c349dbc7Sjsg 	if (node->attr.priority == I915_PRIORITY_INVALID) {
232c349dbc7Sjsg 		GEM_BUG_ON(!list_empty(&node->link));
233c349dbc7Sjsg 		node->attr = *attr;
234c349dbc7Sjsg 
235c349dbc7Sjsg 		if (stack.dfs_link.next == stack.dfs_link.prev)
236c349dbc7Sjsg 			return;
237c349dbc7Sjsg 
238c349dbc7Sjsg 		__list_del_entry(&stack.dfs_link);
239c349dbc7Sjsg 	}
240c349dbc7Sjsg 
241c349dbc7Sjsg 	memset(&cache, 0, sizeof(cache));
2425ca02815Sjsg 	sched_engine = node_to_request(node)->engine->sched_engine;
2435ca02815Sjsg 	spin_lock(&sched_engine->lock);
244c349dbc7Sjsg 
245c349dbc7Sjsg 	/* Fifo and depth-first replacement ensure our deps execute before us */
2465ca02815Sjsg 	sched_engine = lock_sched_engine(node, sched_engine, &cache);
247c349dbc7Sjsg 	list_for_each_entry_safe_reverse(dep, p, &dfs, dfs_link) {
2485ca02815Sjsg 		struct i915_request *from = container_of(dep->signaler,
2495ca02815Sjsg 							 struct i915_request,
2505ca02815Sjsg 							 sched);
251c349dbc7Sjsg 		INIT_LIST_HEAD(&dep->dfs_link);
252c349dbc7Sjsg 
253c349dbc7Sjsg 		node = dep->signaler;
2545ca02815Sjsg 		sched_engine = lock_sched_engine(node, sched_engine, &cache);
2555ca02815Sjsg 		lockdep_assert_held(&sched_engine->lock);
256c349dbc7Sjsg 
257c349dbc7Sjsg 		/* Recheck after acquiring the engine->timeline.lock */
258c349dbc7Sjsg 		if (prio <= node->attr.priority || node_signaled(node))
259c349dbc7Sjsg 			continue;
260c349dbc7Sjsg 
2615ca02815Sjsg 		GEM_BUG_ON(node_to_request(node)->engine->sched_engine !=
2625ca02815Sjsg 			   sched_engine);
2635ca02815Sjsg 
2645ca02815Sjsg 		/* Must be called before changing the nodes priority */
2655ca02815Sjsg 		if (sched_engine->bump_inflight_request_prio)
2665ca02815Sjsg 			sched_engine->bump_inflight_request_prio(from, prio);
267c349dbc7Sjsg 
268c349dbc7Sjsg 		WRITE_ONCE(node->attr.priority, prio);
269c349dbc7Sjsg 
270c349dbc7Sjsg 		/*
271c349dbc7Sjsg 		 * Once the request is ready, it will be placed into the
272c349dbc7Sjsg 		 * priority lists and then onto the HW runlist. Before the
273c349dbc7Sjsg 		 * request is ready, it does not contribute to our preemption
274c349dbc7Sjsg 		 * decisions and we can safely ignore it, as it will, and
275c349dbc7Sjsg 		 * any preemption required, be dealt with upon submission.
276c349dbc7Sjsg 		 * See engine->submit_request()
277c349dbc7Sjsg 		 */
278c349dbc7Sjsg 		if (list_empty(&node->link))
279c349dbc7Sjsg 			continue;
280c349dbc7Sjsg 
281c349dbc7Sjsg 		if (i915_request_in_priority_queue(node_to_request(node))) {
282c349dbc7Sjsg 			if (!cache.priolist)
283c349dbc7Sjsg 				cache.priolist =
2845ca02815Sjsg 					i915_sched_lookup_priolist(sched_engine,
285c349dbc7Sjsg 								   prio);
286c349dbc7Sjsg 			list_move_tail(&node->link, cache.priolist);
287c349dbc7Sjsg 		}
288c349dbc7Sjsg 
289c349dbc7Sjsg 		/* Defer (tasklet) submission until after all of our updates. */
2905ca02815Sjsg 		if (sched_engine->kick_backend)
2915ca02815Sjsg 			sched_engine->kick_backend(node_to_request(node), prio);
292c349dbc7Sjsg 	}
293c349dbc7Sjsg 
2945ca02815Sjsg 	spin_unlock(&sched_engine->lock);
295c349dbc7Sjsg }
296c349dbc7Sjsg 
i915_schedule(struct i915_request * rq,const struct i915_sched_attr * attr)297c349dbc7Sjsg void i915_schedule(struct i915_request *rq, const struct i915_sched_attr *attr)
298c349dbc7Sjsg {
299c349dbc7Sjsg 	spin_lock_irq(&schedule_lock);
300c349dbc7Sjsg 	__i915_schedule(&rq->sched, attr);
301c349dbc7Sjsg 	spin_unlock_irq(&schedule_lock);
302c349dbc7Sjsg }
303c349dbc7Sjsg 
i915_sched_node_init(struct i915_sched_node * node)304c349dbc7Sjsg void i915_sched_node_init(struct i915_sched_node *node)
305c349dbc7Sjsg {
306c349dbc7Sjsg 	INIT_LIST_HEAD(&node->signalers_list);
307c349dbc7Sjsg 	INIT_LIST_HEAD(&node->waiters_list);
308c349dbc7Sjsg 	INIT_LIST_HEAD(&node->link);
309c349dbc7Sjsg 
310c349dbc7Sjsg 	i915_sched_node_reinit(node);
311c349dbc7Sjsg }
312c349dbc7Sjsg 
i915_sched_node_reinit(struct i915_sched_node * node)313c349dbc7Sjsg void i915_sched_node_reinit(struct i915_sched_node *node)
314c349dbc7Sjsg {
315c349dbc7Sjsg 	node->attr.priority = I915_PRIORITY_INVALID;
316c349dbc7Sjsg 	node->semaphores = 0;
317c349dbc7Sjsg 	node->flags = 0;
318c349dbc7Sjsg 
319c349dbc7Sjsg 	GEM_BUG_ON(!list_empty(&node->signalers_list));
320c349dbc7Sjsg 	GEM_BUG_ON(!list_empty(&node->waiters_list));
321c349dbc7Sjsg 	GEM_BUG_ON(!list_empty(&node->link));
322c349dbc7Sjsg }
323c349dbc7Sjsg 
324c349dbc7Sjsg static struct i915_dependency *
i915_dependency_alloc(void)325c349dbc7Sjsg i915_dependency_alloc(void)
326c349dbc7Sjsg {
327c349dbc7Sjsg #ifdef __linux__
3285ca02815Sjsg 	return kmem_cache_alloc(slab_dependencies, GFP_KERNEL);
329c349dbc7Sjsg #else
3305ca02815Sjsg 	return pool_get(&slab_dependencies, PR_WAITOK);
331c349dbc7Sjsg #endif
332c349dbc7Sjsg }
333c349dbc7Sjsg 
334c349dbc7Sjsg static void
i915_dependency_free(struct i915_dependency * dep)335c349dbc7Sjsg i915_dependency_free(struct i915_dependency *dep)
336c349dbc7Sjsg {
337c349dbc7Sjsg #ifdef __linux__
3385ca02815Sjsg 	kmem_cache_free(slab_dependencies, dep);
339c349dbc7Sjsg #else
3405ca02815Sjsg 	pool_put(&slab_dependencies, dep);
341c349dbc7Sjsg #endif
342c349dbc7Sjsg }
343c349dbc7Sjsg 
__i915_sched_node_add_dependency(struct i915_sched_node * node,struct i915_sched_node * signal,struct i915_dependency * dep,unsigned long flags)344c349dbc7Sjsg bool __i915_sched_node_add_dependency(struct i915_sched_node *node,
345c349dbc7Sjsg 				      struct i915_sched_node *signal,
346c349dbc7Sjsg 				      struct i915_dependency *dep,
347c349dbc7Sjsg 				      unsigned long flags)
348c349dbc7Sjsg {
349c349dbc7Sjsg 	bool ret = false;
350c349dbc7Sjsg 
351c349dbc7Sjsg 	spin_lock_irq(&schedule_lock);
352c349dbc7Sjsg 
353c349dbc7Sjsg 	if (!node_signaled(signal)) {
354c349dbc7Sjsg 		INIT_LIST_HEAD(&dep->dfs_link);
355c349dbc7Sjsg 		dep->signaler = signal;
356c349dbc7Sjsg 		dep->waiter = node;
357c349dbc7Sjsg 		dep->flags = flags;
358c349dbc7Sjsg 
359c349dbc7Sjsg 		/* All set, now publish. Beware the lockless walkers. */
360c349dbc7Sjsg 		list_add_rcu(&dep->signal_link, &node->signalers_list);
361c349dbc7Sjsg 		list_add_rcu(&dep->wait_link, &signal->waiters_list);
362c349dbc7Sjsg 
363ad8b1aafSjsg 		/* Propagate the chains */
364ad8b1aafSjsg 		node->flags |= signal->flags;
365c349dbc7Sjsg 		ret = true;
366c349dbc7Sjsg 	}
367c349dbc7Sjsg 
368c349dbc7Sjsg 	spin_unlock_irq(&schedule_lock);
369c349dbc7Sjsg 
370c349dbc7Sjsg 	return ret;
371c349dbc7Sjsg }
372c349dbc7Sjsg 
i915_sched_node_add_dependency(struct i915_sched_node * node,struct i915_sched_node * signal,unsigned long flags)373c349dbc7Sjsg int i915_sched_node_add_dependency(struct i915_sched_node *node,
374c349dbc7Sjsg 				   struct i915_sched_node *signal,
375c349dbc7Sjsg 				   unsigned long flags)
376c349dbc7Sjsg {
377c349dbc7Sjsg 	struct i915_dependency *dep;
378c349dbc7Sjsg 
379c349dbc7Sjsg 	dep = i915_dependency_alloc();
380c349dbc7Sjsg 	if (!dep)
381c349dbc7Sjsg 		return -ENOMEM;
382c349dbc7Sjsg 
383c349dbc7Sjsg 	if (!__i915_sched_node_add_dependency(node, signal, dep,
384c349dbc7Sjsg 					      flags | I915_DEPENDENCY_ALLOC))
385c349dbc7Sjsg 		i915_dependency_free(dep);
386c349dbc7Sjsg 
387c349dbc7Sjsg 	return 0;
388c349dbc7Sjsg }
389c349dbc7Sjsg 
i915_sched_node_fini(struct i915_sched_node * node)390c349dbc7Sjsg void i915_sched_node_fini(struct i915_sched_node *node)
391c349dbc7Sjsg {
392c349dbc7Sjsg 	struct i915_dependency *dep, *tmp;
393c349dbc7Sjsg 
394c349dbc7Sjsg 	spin_lock_irq(&schedule_lock);
395c349dbc7Sjsg 
396c349dbc7Sjsg 	/*
397c349dbc7Sjsg 	 * Everyone we depended upon (the fences we wait to be signaled)
398c349dbc7Sjsg 	 * should retire before us and remove themselves from our list.
399c349dbc7Sjsg 	 * However, retirement is run independently on each timeline and
400c349dbc7Sjsg 	 * so we may be called out-of-order.
401c349dbc7Sjsg 	 */
402c349dbc7Sjsg 	list_for_each_entry_safe(dep, tmp, &node->signalers_list, signal_link) {
403c349dbc7Sjsg 		GEM_BUG_ON(!list_empty(&dep->dfs_link));
404c349dbc7Sjsg 
405c349dbc7Sjsg 		list_del_rcu(&dep->wait_link);
406c349dbc7Sjsg 		if (dep->flags & I915_DEPENDENCY_ALLOC)
407c349dbc7Sjsg 			i915_dependency_free(dep);
408c349dbc7Sjsg 	}
409c349dbc7Sjsg 	INIT_LIST_HEAD(&node->signalers_list);
410c349dbc7Sjsg 
411c349dbc7Sjsg 	/* Remove ourselves from everyone who depends upon us */
412c349dbc7Sjsg 	list_for_each_entry_safe(dep, tmp, &node->waiters_list, wait_link) {
413c349dbc7Sjsg 		GEM_BUG_ON(dep->signaler != node);
414c349dbc7Sjsg 		GEM_BUG_ON(!list_empty(&dep->dfs_link));
415c349dbc7Sjsg 
416c349dbc7Sjsg 		list_del_rcu(&dep->signal_link);
417c349dbc7Sjsg 		if (dep->flags & I915_DEPENDENCY_ALLOC)
418c349dbc7Sjsg 			i915_dependency_free(dep);
419c349dbc7Sjsg 	}
420c349dbc7Sjsg 	INIT_LIST_HEAD(&node->waiters_list);
421c349dbc7Sjsg 
422c349dbc7Sjsg 	spin_unlock_irq(&schedule_lock);
423c349dbc7Sjsg }
424c349dbc7Sjsg 
i915_request_show_with_schedule(struct drm_printer * m,const struct i915_request * rq,const char * prefix,int indent)4255ca02815Sjsg void i915_request_show_with_schedule(struct drm_printer *m,
4265ca02815Sjsg 				     const struct i915_request *rq,
4275ca02815Sjsg 				     const char *prefix,
4285ca02815Sjsg 				     int indent)
429c349dbc7Sjsg {
4305ca02815Sjsg 	struct i915_dependency *dep;
4315ca02815Sjsg 
4325ca02815Sjsg 	i915_request_show(m, rq, prefix, indent);
4335ca02815Sjsg 	if (i915_request_completed(rq))
4345ca02815Sjsg 		return;
4355ca02815Sjsg 
4365ca02815Sjsg 	rcu_read_lock();
4375ca02815Sjsg 	for_each_signaler(dep, rq) {
4385ca02815Sjsg 		const struct i915_request *signaler =
4395ca02815Sjsg 			node_to_request(dep->signaler);
4405ca02815Sjsg 
4415ca02815Sjsg 		/* Dependencies along the same timeline are expected. */
4425ca02815Sjsg 		if (signaler->timeline == rq->timeline)
4435ca02815Sjsg 			continue;
4445ca02815Sjsg 
4455ca02815Sjsg 		if (__i915_request_is_complete(signaler))
4465ca02815Sjsg 			continue;
4475ca02815Sjsg 
4485ca02815Sjsg 		i915_request_show(m, signaler, prefix, indent + 2);
4495ca02815Sjsg 	}
4505ca02815Sjsg 	rcu_read_unlock();
451c349dbc7Sjsg }
452c349dbc7Sjsg 
default_destroy(struct kref * kref)4535ca02815Sjsg static void default_destroy(struct kref *kref)
4545ca02815Sjsg {
4555ca02815Sjsg 	struct i915_sched_engine *sched_engine =
4565ca02815Sjsg 		container_of(kref, typeof(*sched_engine), ref);
4575ca02815Sjsg 
4585ca02815Sjsg 	tasklet_kill(&sched_engine->tasklet); /* flush the callback */
4595ca02815Sjsg 	kfree(sched_engine);
4605ca02815Sjsg }
4615ca02815Sjsg 
default_disabled(struct i915_sched_engine * sched_engine)4625ca02815Sjsg static bool default_disabled(struct i915_sched_engine *sched_engine)
4635ca02815Sjsg {
4645ca02815Sjsg 	return false;
4655ca02815Sjsg }
4665ca02815Sjsg 
4675ca02815Sjsg struct i915_sched_engine *
i915_sched_engine_create(unsigned int subclass)4685ca02815Sjsg i915_sched_engine_create(unsigned int subclass)
4695ca02815Sjsg {
4705ca02815Sjsg 	struct i915_sched_engine *sched_engine;
4715ca02815Sjsg 
4725ca02815Sjsg 	sched_engine = kzalloc(sizeof(*sched_engine), GFP_KERNEL);
4735ca02815Sjsg 	if (!sched_engine)
4745ca02815Sjsg 		return NULL;
4755ca02815Sjsg 
4765ca02815Sjsg 	kref_init(&sched_engine->ref);
4775ca02815Sjsg 
4785ca02815Sjsg 	sched_engine->queue = RB_ROOT_CACHED;
4795ca02815Sjsg 	sched_engine->queue_priority_hint = INT_MIN;
4805ca02815Sjsg 	sched_engine->destroy = default_destroy;
4815ca02815Sjsg 	sched_engine->disabled = default_disabled;
4825ca02815Sjsg 
4835ca02815Sjsg 	INIT_LIST_HEAD(&sched_engine->requests);
4845ca02815Sjsg 	INIT_LIST_HEAD(&sched_engine->hold);
4855ca02815Sjsg 
486*ebc65071Sjsg 	mtx_init(&sched_engine->lock, IPL_TTY);
4875ca02815Sjsg 	lockdep_set_subclass(&sched_engine->lock, subclass);
4885ca02815Sjsg 
4895ca02815Sjsg 	/*
4905ca02815Sjsg 	 * Due to an interesting quirk in lockdep's internal debug tracking,
4915ca02815Sjsg 	 * after setting a subclass we must ensure the lock is used. Otherwise,
4925ca02815Sjsg 	 * nr_unused_locks is incremented once too often.
4935ca02815Sjsg 	 */
4945ca02815Sjsg #ifdef CONFIG_DEBUG_LOCK_ALLOC
4955ca02815Sjsg 	local_irq_disable();
4965ca02815Sjsg 	lock_map_acquire(&sched_engine->lock.dep_map);
4975ca02815Sjsg 	lock_map_release(&sched_engine->lock.dep_map);
4985ca02815Sjsg 	local_irq_enable();
4995ca02815Sjsg #endif
5005ca02815Sjsg 
5015ca02815Sjsg 	return sched_engine;
5025ca02815Sjsg }
5035ca02815Sjsg 
i915_scheduler_module_exit(void)5045ca02815Sjsg void i915_scheduler_module_exit(void)
505c349dbc7Sjsg {
506c349dbc7Sjsg #ifdef __linux__
5075ca02815Sjsg 	kmem_cache_destroy(slab_dependencies);
5085ca02815Sjsg 	kmem_cache_destroy(slab_priorities);
509c349dbc7Sjsg #else
5105ca02815Sjsg 	pool_destroy(&slab_dependencies);
5115ca02815Sjsg 	pool_destroy(&slab_priorities);
512c349dbc7Sjsg #endif
513c349dbc7Sjsg }
514c349dbc7Sjsg 
i915_scheduler_module_init(void)5155ca02815Sjsg int __init i915_scheduler_module_init(void)
516c349dbc7Sjsg {
517c349dbc7Sjsg #ifdef __linux__
5185ca02815Sjsg 	slab_dependencies = KMEM_CACHE(i915_dependency,
519c349dbc7Sjsg 					      SLAB_HWCACHE_ALIGN |
520c349dbc7Sjsg 					      SLAB_TYPESAFE_BY_RCU);
5215ca02815Sjsg 	if (!slab_dependencies)
522c349dbc7Sjsg 		return -ENOMEM;
523c349dbc7Sjsg 
5245ca02815Sjsg 	slab_priorities = KMEM_CACHE(i915_priolist, 0);
5255ca02815Sjsg 	if (!slab_priorities)
526c349dbc7Sjsg 		goto err_priorities;
527c349dbc7Sjsg 
528c349dbc7Sjsg 	return 0;
529c349dbc7Sjsg 
530c349dbc7Sjsg err_priorities:
5315ca02815Sjsg 	kmem_cache_destroy(slab_priorities);
532c349dbc7Sjsg 	return -ENOMEM;
533c349dbc7Sjsg #else
5345ca02815Sjsg 	pool_init(&slab_dependencies, sizeof(struct i915_dependency),
5350f557061Sjsg 	    CACHELINESIZE, IPL_TTY, 0, "gsdep", NULL);
5365ca02815Sjsg 	pool_init(&slab_priorities, sizeof(struct i915_priolist),
5370f557061Sjsg 	    CACHELINESIZE, IPL_TTY, 0, "gspri", NULL);
538c349dbc7Sjsg 
539c349dbc7Sjsg 	return 0;
540c349dbc7Sjsg #endif
541c349dbc7Sjsg }
542