xref: /dflybsd-src/sys/dev/drm/scheduler/gpu_scheduler.c (revision 789731325bde747251c28a37e0a00ed4efb88c46)
1b843c749SSergey Zigachev /*
2b843c749SSergey Zigachev  * Copyright 2015 Advanced Micro Devices, Inc.
3b843c749SSergey Zigachev  *
4b843c749SSergey Zigachev  * Permission is hereby granted, free of charge, to any person obtaining a
5b843c749SSergey Zigachev  * copy of this software and associated documentation files (the "Software"),
6b843c749SSergey Zigachev  * to deal in the Software without restriction, including without limitation
7b843c749SSergey Zigachev  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8b843c749SSergey Zigachev  * and/or sell copies of the Software, and to permit persons to whom the
9b843c749SSergey Zigachev  * Software is furnished to do so, subject to the following conditions:
10b843c749SSergey Zigachev  *
11b843c749SSergey Zigachev  * The above copyright notice and this permission notice shall be included in
12b843c749SSergey Zigachev  * all copies or substantial portions of the Software.
13b843c749SSergey Zigachev  *
14b843c749SSergey Zigachev  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15b843c749SSergey Zigachev  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16b843c749SSergey Zigachev  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17b843c749SSergey Zigachev  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18b843c749SSergey Zigachev  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19b843c749SSergey Zigachev  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20b843c749SSergey Zigachev  * OTHER DEALINGS IN THE SOFTWARE.
21b843c749SSergey Zigachev  *
22b843c749SSergey Zigachev  */
23b843c749SSergey Zigachev 
24b843c749SSergey Zigachev /**
25b843c749SSergey Zigachev  * DOC: Overview
26b843c749SSergey Zigachev  *
27b843c749SSergey Zigachev  * The GPU scheduler provides entities which allow userspace to push jobs
28b843c749SSergey Zigachev  * into software queues which are then scheduled on a hardware run queue.
29b843c749SSergey Zigachev  * The software queues have a priority among them. The scheduler selects the entities
30b843c749SSergey Zigachev  * from the run queue using a FIFO. The scheduler provides dependency handling
31b843c749SSergey Zigachev  * features among jobs. The driver is supposed to provide callback functions for
32b843c749SSergey Zigachev  * backend operations to the scheduler like submitting a job to hardware run queue,
33b843c749SSergey Zigachev  * returning the dependencies of a job etc.
34b843c749SSergey Zigachev  *
35b843c749SSergey Zigachev  * The organisation of the scheduler is the following:
36b843c749SSergey Zigachev  *
37b843c749SSergey Zigachev  * 1. Each hw run queue has one scheduler
38b843c749SSergey Zigachev  * 2. Each scheduler has multiple run queues with different priorities
39b843c749SSergey Zigachev  *    (e.g., HIGH_HW,HIGH_SW, KERNEL, NORMAL)
40b843c749SSergey Zigachev  * 3. Each scheduler run queue has a queue of entities to schedule
41b843c749SSergey Zigachev  * 4. Entities themselves maintain a queue of jobs that will be scheduled on
42b843c749SSergey Zigachev  *    the hardware.
43b843c749SSergey Zigachev  *
44b843c749SSergey Zigachev  * The jobs in a entity are always scheduled in the order that they were pushed.
45b843c749SSergey Zigachev  */
46b843c749SSergey Zigachev 
47b843c749SSergey Zigachev #include <linux/kthread.h>
48b843c749SSergey Zigachev #include <linux/wait.h>
49b843c749SSergey Zigachev #include <linux/sched.h>
50b843c749SSergey Zigachev #include <uapi/linux/sched/types.h>
51b843c749SSergey Zigachev #include <drm/drmP.h>
52b843c749SSergey Zigachev #include <drm/gpu_scheduler.h>
53b843c749SSergey Zigachev #include <drm/spsc_queue.h>
54b843c749SSergey Zigachev 
55b843c749SSergey Zigachev #define CREATE_TRACE_POINTS
56b843c749SSergey Zigachev #include "gpu_scheduler_trace.h"
57b843c749SSergey Zigachev 
58b843c749SSergey Zigachev #define to_drm_sched_job(sched_job)		\
59b843c749SSergey Zigachev 		container_of((sched_job), struct drm_sched_job, queue_node)
60b843c749SSergey Zigachev 
61b843c749SSergey Zigachev static bool drm_sched_entity_is_ready(struct drm_sched_entity *entity);
62b843c749SSergey Zigachev static void drm_sched_wakeup(struct drm_gpu_scheduler *sched);
63b843c749SSergey Zigachev static void drm_sched_process_job(struct dma_fence *f, struct dma_fence_cb *cb);
64b843c749SSergey Zigachev 
65b843c749SSergey Zigachev /**
66b843c749SSergey Zigachev  * drm_sched_rq_init - initialize a given run queue struct
67b843c749SSergey Zigachev  *
68b843c749SSergey Zigachev  * @rq: scheduler run queue
69b843c749SSergey Zigachev  *
70b843c749SSergey Zigachev  * Initializes a scheduler runqueue.
71b843c749SSergey Zigachev  */
drm_sched_rq_init(struct drm_gpu_scheduler * sched,struct drm_sched_rq * rq)72b843c749SSergey Zigachev static void drm_sched_rq_init(struct drm_gpu_scheduler *sched,
73b843c749SSergey Zigachev 			      struct drm_sched_rq *rq)
74b843c749SSergey Zigachev {
75*78973132SSergey Zigachev 	spin_init(&rq->lock, "dsrql");
76b843c749SSergey Zigachev 	INIT_LIST_HEAD(&rq->entities);
77b843c749SSergey Zigachev 	rq->current_entity = NULL;
78b843c749SSergey Zigachev 	rq->sched = sched;
79b843c749SSergey Zigachev }
80b843c749SSergey Zigachev 
81b843c749SSergey Zigachev /**
82b843c749SSergey Zigachev  * drm_sched_rq_add_entity - add an entity
83b843c749SSergey Zigachev  *
84b843c749SSergey Zigachev  * @rq: scheduler run queue
85b843c749SSergey Zigachev  * @entity: scheduler entity
86b843c749SSergey Zigachev  *
87b843c749SSergey Zigachev  * Adds a scheduler entity to the run queue.
88b843c749SSergey Zigachev  */
drm_sched_rq_add_entity(struct drm_sched_rq * rq,struct drm_sched_entity * entity)89b843c749SSergey Zigachev static void drm_sched_rq_add_entity(struct drm_sched_rq *rq,
90b843c749SSergey Zigachev 				    struct drm_sched_entity *entity)
91b843c749SSergey Zigachev {
92b843c749SSergey Zigachev 	if (!list_empty(&entity->list))
93b843c749SSergey Zigachev 		return;
94b843c749SSergey Zigachev 	spin_lock(&rq->lock);
95b843c749SSergey Zigachev 	list_add_tail(&entity->list, &rq->entities);
96b843c749SSergey Zigachev 	spin_unlock(&rq->lock);
97b843c749SSergey Zigachev }
98b843c749SSergey Zigachev 
99b843c749SSergey Zigachev /**
100b843c749SSergey Zigachev  * drm_sched_rq_remove_entity - remove an entity
101b843c749SSergey Zigachev  *
102b843c749SSergey Zigachev  * @rq: scheduler run queue
103b843c749SSergey Zigachev  * @entity: scheduler entity
104b843c749SSergey Zigachev  *
105b843c749SSergey Zigachev  * Removes a scheduler entity from the run queue.
106b843c749SSergey Zigachev  */
drm_sched_rq_remove_entity(struct drm_sched_rq * rq,struct drm_sched_entity * entity)107b843c749SSergey Zigachev static void drm_sched_rq_remove_entity(struct drm_sched_rq *rq,
108b843c749SSergey Zigachev 				       struct drm_sched_entity *entity)
109b843c749SSergey Zigachev {
110b843c749SSergey Zigachev 	if (list_empty(&entity->list))
111b843c749SSergey Zigachev 		return;
112b843c749SSergey Zigachev 	spin_lock(&rq->lock);
113b843c749SSergey Zigachev 	list_del_init(&entity->list);
114b843c749SSergey Zigachev 	if (rq->current_entity == entity)
115b843c749SSergey Zigachev 		rq->current_entity = NULL;
116b843c749SSergey Zigachev 	spin_unlock(&rq->lock);
117b843c749SSergey Zigachev }
118b843c749SSergey Zigachev 
119b843c749SSergey Zigachev /**
120b843c749SSergey Zigachev  * drm_sched_rq_select_entity - Select an entity which could provide a job to run
121b843c749SSergey Zigachev  *
122b843c749SSergey Zigachev  * @rq: scheduler run queue to check.
123b843c749SSergey Zigachev  *
124b843c749SSergey Zigachev  * Try to find a ready entity, returns NULL if none found.
125b843c749SSergey Zigachev  */
126b843c749SSergey Zigachev static struct drm_sched_entity *
drm_sched_rq_select_entity(struct drm_sched_rq * rq)127b843c749SSergey Zigachev drm_sched_rq_select_entity(struct drm_sched_rq *rq)
128b843c749SSergey Zigachev {
129b843c749SSergey Zigachev 	struct drm_sched_entity *entity;
130b843c749SSergey Zigachev 
131b843c749SSergey Zigachev 	spin_lock(&rq->lock);
132b843c749SSergey Zigachev 
133b843c749SSergey Zigachev 	entity = rq->current_entity;
134b843c749SSergey Zigachev 	if (entity) {
135b843c749SSergey Zigachev 		list_for_each_entry_continue(entity, &rq->entities, list) {
136b843c749SSergey Zigachev 			if (drm_sched_entity_is_ready(entity)) {
137b843c749SSergey Zigachev 				rq->current_entity = entity;
138b843c749SSergey Zigachev 				spin_unlock(&rq->lock);
139b843c749SSergey Zigachev 				return entity;
140b843c749SSergey Zigachev 			}
141b843c749SSergey Zigachev 		}
142b843c749SSergey Zigachev 	}
143b843c749SSergey Zigachev 
144b843c749SSergey Zigachev 	list_for_each_entry(entity, &rq->entities, list) {
145b843c749SSergey Zigachev 
146b843c749SSergey Zigachev 		if (drm_sched_entity_is_ready(entity)) {
147b843c749SSergey Zigachev 			rq->current_entity = entity;
148b843c749SSergey Zigachev 			spin_unlock(&rq->lock);
149b843c749SSergey Zigachev 			return entity;
150b843c749SSergey Zigachev 		}
151b843c749SSergey Zigachev 
152b843c749SSergey Zigachev 		if (entity == rq->current_entity)
153b843c749SSergey Zigachev 			break;
154b843c749SSergey Zigachev 	}
155b843c749SSergey Zigachev 
156b843c749SSergey Zigachev 	spin_unlock(&rq->lock);
157b843c749SSergey Zigachev 
158b843c749SSergey Zigachev 	return NULL;
159b843c749SSergey Zigachev }
160b843c749SSergey Zigachev 
161b843c749SSergey Zigachev /**
162b843c749SSergey Zigachev  * drm_sched_entity_init - Init a context entity used by scheduler when
163b843c749SSergey Zigachev  * submit to HW ring.
164b843c749SSergey Zigachev  *
165b843c749SSergey Zigachev  * @entity: scheduler entity to init
166b843c749SSergey Zigachev  * @rq_list: the list of run queue on which jobs from this
167b843c749SSergey Zigachev  *           entity can be submitted
168b843c749SSergey Zigachev  * @num_rq_list: number of run queue in rq_list
169b843c749SSergey Zigachev  * @guilty: atomic_t set to 1 when a job on this queue
170b843c749SSergey Zigachev  *          is found to be guilty causing a timeout
171b843c749SSergey Zigachev  *
172b843c749SSergey Zigachev  * Note: the rq_list should have atleast one element to schedule
173b843c749SSergey Zigachev  *       the entity
174b843c749SSergey Zigachev  *
175b843c749SSergey Zigachev  * Returns 0 on success or a negative error code on failure.
176b843c749SSergey Zigachev */
drm_sched_entity_init(struct drm_sched_entity * entity,struct drm_sched_rq ** rq_list,unsigned int num_rq_list,atomic_t * guilty)177b843c749SSergey Zigachev int drm_sched_entity_init(struct drm_sched_entity *entity,
178b843c749SSergey Zigachev 			  struct drm_sched_rq **rq_list,
179b843c749SSergey Zigachev 			  unsigned int num_rq_list,
180b843c749SSergey Zigachev 			  atomic_t *guilty)
181b843c749SSergey Zigachev {
182b843c749SSergey Zigachev 	if (!(entity && rq_list && num_rq_list > 0 && rq_list[0]))
183b843c749SSergey Zigachev 		return -EINVAL;
184b843c749SSergey Zigachev 
185b843c749SSergey Zigachev 	memset(entity, 0, sizeof(struct drm_sched_entity));
186b843c749SSergey Zigachev 	INIT_LIST_HEAD(&entity->list);
187b843c749SSergey Zigachev 	entity->rq = rq_list[0];
188b843c749SSergey Zigachev 	entity->guilty = guilty;
189b843c749SSergey Zigachev 	entity->last_scheduled = NULL;
190b843c749SSergey Zigachev 
191*78973132SSergey Zigachev 	spin_init(&entity->rq_lock, "dserql");
192b843c749SSergey Zigachev 	spsc_queue_init(&entity->job_queue);
193b843c749SSergey Zigachev 
194b843c749SSergey Zigachev 	atomic_set(&entity->fence_seq, 0);
195b843c749SSergey Zigachev 	entity->fence_context = dma_fence_context_alloc(2);
196b843c749SSergey Zigachev 
197b843c749SSergey Zigachev 	return 0;
198b843c749SSergey Zigachev }
199b843c749SSergey Zigachev EXPORT_SYMBOL(drm_sched_entity_init);
200b843c749SSergey Zigachev 
201b843c749SSergey Zigachev /**
202b843c749SSergey Zigachev  * drm_sched_entity_is_idle - Check if entity is idle
203b843c749SSergey Zigachev  *
204b843c749SSergey Zigachev  * @entity: scheduler entity
205b843c749SSergey Zigachev  *
206b843c749SSergey Zigachev  * Returns true if the entity does not have any unscheduled jobs.
207b843c749SSergey Zigachev  */
drm_sched_entity_is_idle(struct drm_sched_entity * entity)208b843c749SSergey Zigachev static bool drm_sched_entity_is_idle(struct drm_sched_entity *entity)
209b843c749SSergey Zigachev {
210b843c749SSergey Zigachev 	rmb();
211b843c749SSergey Zigachev 
212b843c749SSergey Zigachev 	if (list_empty(&entity->list) ||
213b843c749SSergey Zigachev 	    spsc_queue_peek(&entity->job_queue) == NULL)
214b843c749SSergey Zigachev 		return true;
215b843c749SSergey Zigachev 
216b843c749SSergey Zigachev 	return false;
217b843c749SSergey Zigachev }
218b843c749SSergey Zigachev 
219b843c749SSergey Zigachev /**
220b843c749SSergey Zigachev  * drm_sched_entity_is_ready - Check if entity is ready
221b843c749SSergey Zigachev  *
222b843c749SSergey Zigachev  * @entity: scheduler entity
223b843c749SSergey Zigachev  *
224b843c749SSergey Zigachev  * Return true if entity could provide a job.
225b843c749SSergey Zigachev  */
drm_sched_entity_is_ready(struct drm_sched_entity * entity)226b843c749SSergey Zigachev static bool drm_sched_entity_is_ready(struct drm_sched_entity *entity)
227b843c749SSergey Zigachev {
228b843c749SSergey Zigachev 	if (spsc_queue_peek(&entity->job_queue) == NULL)
229b843c749SSergey Zigachev 		return false;
230b843c749SSergey Zigachev 
231b843c749SSergey Zigachev 	if (READ_ONCE(entity->dependency))
232b843c749SSergey Zigachev 		return false;
233b843c749SSergey Zigachev 
234b843c749SSergey Zigachev 	return true;
235b843c749SSergey Zigachev }
236b843c749SSergey Zigachev 
drm_sched_entity_kill_jobs_cb(struct dma_fence * f,struct dma_fence_cb * cb)237b843c749SSergey Zigachev static void drm_sched_entity_kill_jobs_cb(struct dma_fence *f,
238b843c749SSergey Zigachev 				    struct dma_fence_cb *cb)
239b843c749SSergey Zigachev {
240b843c749SSergey Zigachev 	struct drm_sched_job *job = container_of(cb, struct drm_sched_job,
241b843c749SSergey Zigachev 						 finish_cb);
242b843c749SSergey Zigachev 	drm_sched_fence_finished(job->s_fence);
243b843c749SSergey Zigachev 	WARN_ON(job->s_fence->parent);
244b843c749SSergey Zigachev 	dma_fence_put(&job->s_fence->finished);
245b843c749SSergey Zigachev 	job->sched->ops->free_job(job);
246b843c749SSergey Zigachev }
247b843c749SSergey Zigachev 
248b843c749SSergey Zigachev 
249b843c749SSergey Zigachev /**
250b843c749SSergey Zigachev  * drm_sched_entity_flush - Flush a context entity
251b843c749SSergey Zigachev  *
252b843c749SSergey Zigachev  * @entity: scheduler entity
253b843c749SSergey Zigachev  * @timeout: time to wait in for Q to become empty in jiffies.
254b843c749SSergey Zigachev  *
255b843c749SSergey Zigachev  * Splitting drm_sched_entity_fini() into two functions, The first one does the waiting,
256b843c749SSergey Zigachev  * removes the entity from the runqueue and returns an error when the process was killed.
257b843c749SSergey Zigachev  *
258b843c749SSergey Zigachev  * Returns the remaining time in jiffies left from the input timeout
259b843c749SSergey Zigachev  */
drm_sched_entity_flush(struct drm_sched_entity * entity,long timeout)260b843c749SSergey Zigachev long drm_sched_entity_flush(struct drm_sched_entity *entity, long timeout)
261b843c749SSergey Zigachev {
262b843c749SSergey Zigachev 	struct drm_gpu_scheduler *sched;
263*78973132SSergey Zigachev #if 0
264b843c749SSergey Zigachev 	struct task_struct *last_user;
265*78973132SSergey Zigachev #endif
266b843c749SSergey Zigachev 	long ret = timeout;
267b843c749SSergey Zigachev 
268b843c749SSergey Zigachev 	sched = entity->rq->sched;
269b843c749SSergey Zigachev 	/**
270b843c749SSergey Zigachev 	 * The client will not queue more IBs during this fini, consume existing
271b843c749SSergey Zigachev 	 * queued IBs or discard them on SIGKILL
272b843c749SSergey Zigachev 	*/
273*78973132SSergey Zigachev 	if (current->dfly_td->td_flags & TDF_EXITING) {
274*78973132SSergey Zigachev 		if (timeout) {
275b843c749SSergey Zigachev 			ret = wait_event_timeout(
276b843c749SSergey Zigachev 					sched->job_scheduled,
277b843c749SSergey Zigachev 					drm_sched_entity_is_idle(entity),
278b843c749SSergey Zigachev 					timeout);
279*78973132SSergey Zigachev 		}
280*78973132SSergey Zigachev 	} else {
281*78973132SSergey Zigachev 		wait_event_interruptible(sched->job_scheduled, drm_sched_entity_is_idle(entity));
282*78973132SSergey Zigachev 	}
283b843c749SSergey Zigachev 
284b843c749SSergey Zigachev 
285b843c749SSergey Zigachev 	/* For killed process disable any more IBs enqueue right now */
286*78973132SSergey Zigachev #if 0
287b843c749SSergey Zigachev 	last_user = cmpxchg(&entity->last_user, current->group_leader, NULL);
288*78973132SSergey Zigachev #endif
289*78973132SSergey Zigachev 	if (/*(!last_user || last_user == current->group_leader) && */
290*78973132SSergey Zigachev 	    (current->dfly_td->td_flags & TDF_EXITING) && fatal_signal_pending(current))
291b843c749SSergey Zigachev 		drm_sched_rq_remove_entity(entity->rq, entity);
292b843c749SSergey Zigachev 
293b843c749SSergey Zigachev 	return ret;
294b843c749SSergey Zigachev }
295b843c749SSergey Zigachev EXPORT_SYMBOL(drm_sched_entity_flush);
296b843c749SSergey Zigachev 
297b843c749SSergey Zigachev /**
298b843c749SSergey Zigachev  * drm_sched_entity_cleanup - Destroy a context entity
299b843c749SSergey Zigachev  *
300b843c749SSergey Zigachev  * @entity: scheduler entity
301b843c749SSergey Zigachev  *
302b843c749SSergey Zigachev  * This should be called after @drm_sched_entity_do_release. It goes over the
303b843c749SSergey Zigachev  * entity and signals all jobs with an error code if the process was killed.
304b843c749SSergey Zigachev  *
305b843c749SSergey Zigachev  */
drm_sched_entity_fini(struct drm_sched_entity * entity)306b843c749SSergey Zigachev void drm_sched_entity_fini(struct drm_sched_entity *entity)
307b843c749SSergey Zigachev {
308b843c749SSergey Zigachev 	struct drm_gpu_scheduler *sched;
309b843c749SSergey Zigachev 
310b843c749SSergey Zigachev 	sched = entity->rq->sched;
311b843c749SSergey Zigachev 	drm_sched_rq_remove_entity(entity->rq, entity);
312b843c749SSergey Zigachev 
313b843c749SSergey Zigachev 	/* Consumption of existing IBs wasn't completed. Forcefully
314b843c749SSergey Zigachev 	 * remove them here.
315b843c749SSergey Zigachev 	 */
316b843c749SSergey Zigachev 	if (spsc_queue_peek(&entity->job_queue)) {
317b843c749SSergey Zigachev 		struct drm_sched_job *job;
318b843c749SSergey Zigachev 		int r;
319b843c749SSergey Zigachev 
320b843c749SSergey Zigachev 		/* Park the kernel for a moment to make sure it isn't processing
321b843c749SSergey Zigachev 		 * our enity.
322b843c749SSergey Zigachev 		 */
323b843c749SSergey Zigachev 		kthread_park(sched->thread);
324b843c749SSergey Zigachev 		kthread_unpark(sched->thread);
325b843c749SSergey Zigachev 		if (entity->dependency) {
326b843c749SSergey Zigachev 			dma_fence_remove_callback(entity->dependency,
327b843c749SSergey Zigachev 						  &entity->cb);
328b843c749SSergey Zigachev 			dma_fence_put(entity->dependency);
329b843c749SSergey Zigachev 			entity->dependency = NULL;
330b843c749SSergey Zigachev 		}
331b843c749SSergey Zigachev 
332b843c749SSergey Zigachev 		while ((job = to_drm_sched_job(spsc_queue_pop(&entity->job_queue)))) {
333b843c749SSergey Zigachev 			struct drm_sched_fence *s_fence = job->s_fence;
334b843c749SSergey Zigachev 			drm_sched_fence_scheduled(s_fence);
335b843c749SSergey Zigachev 			dma_fence_set_error(&s_fence->finished, -ESRCH);
336b843c749SSergey Zigachev 
337b843c749SSergey Zigachev 			/*
338b843c749SSergey Zigachev 			 * When pipe is hanged by older entity, new entity might
339b843c749SSergey Zigachev 			 * not even have chance to submit it's first job to HW
340b843c749SSergey Zigachev 			 * and so entity->last_scheduled will remain NULL
341b843c749SSergey Zigachev 			 */
342b843c749SSergey Zigachev 			if (!entity->last_scheduled) {
343b843c749SSergey Zigachev 				drm_sched_entity_kill_jobs_cb(NULL, &job->finish_cb);
344b843c749SSergey Zigachev 			} else {
345b843c749SSergey Zigachev 				r = dma_fence_add_callback(entity->last_scheduled, &job->finish_cb,
346b843c749SSergey Zigachev 								drm_sched_entity_kill_jobs_cb);
347b843c749SSergey Zigachev 				if (r == -ENOENT)
348b843c749SSergey Zigachev 					drm_sched_entity_kill_jobs_cb(NULL, &job->finish_cb);
349b843c749SSergey Zigachev 				else if (r)
350b843c749SSergey Zigachev 					DRM_ERROR("fence add callback failed (%d)\n", r);
351b843c749SSergey Zigachev 			}
352b843c749SSergey Zigachev 		}
353b843c749SSergey Zigachev 	}
354b843c749SSergey Zigachev 
355b843c749SSergey Zigachev 	dma_fence_put(entity->last_scheduled);
356b843c749SSergey Zigachev 	entity->last_scheduled = NULL;
357b843c749SSergey Zigachev }
358b843c749SSergey Zigachev EXPORT_SYMBOL(drm_sched_entity_fini);
359b843c749SSergey Zigachev 
360b843c749SSergey Zigachev /**
361b843c749SSergey Zigachev  * drm_sched_entity_fini - Destroy a context entity
362b843c749SSergey Zigachev  *
363b843c749SSergey Zigachev  * @entity: scheduler entity
364b843c749SSergey Zigachev  *
365b843c749SSergey Zigachev  * Calls drm_sched_entity_do_release() and drm_sched_entity_cleanup()
366b843c749SSergey Zigachev  */
drm_sched_entity_destroy(struct drm_sched_entity * entity)367b843c749SSergey Zigachev void drm_sched_entity_destroy(struct drm_sched_entity *entity)
368b843c749SSergey Zigachev {
369b843c749SSergey Zigachev 	drm_sched_entity_flush(entity, MAX_WAIT_SCHED_ENTITY_Q_EMPTY);
370b843c749SSergey Zigachev 	drm_sched_entity_fini(entity);
371b843c749SSergey Zigachev }
372b843c749SSergey Zigachev EXPORT_SYMBOL(drm_sched_entity_destroy);
373b843c749SSergey Zigachev 
drm_sched_entity_wakeup(struct dma_fence * f,struct dma_fence_cb * cb)374b843c749SSergey Zigachev static void drm_sched_entity_wakeup(struct dma_fence *f, struct dma_fence_cb *cb)
375b843c749SSergey Zigachev {
376b843c749SSergey Zigachev 	struct drm_sched_entity *entity =
377b843c749SSergey Zigachev 		container_of(cb, struct drm_sched_entity, cb);
378b843c749SSergey Zigachev 	entity->dependency = NULL;
379b843c749SSergey Zigachev 	dma_fence_put(f);
380b843c749SSergey Zigachev 	drm_sched_wakeup(entity->rq->sched);
381b843c749SSergey Zigachev }
382b843c749SSergey Zigachev 
drm_sched_entity_clear_dep(struct dma_fence * f,struct dma_fence_cb * cb)383b843c749SSergey Zigachev static void drm_sched_entity_clear_dep(struct dma_fence *f, struct dma_fence_cb *cb)
384b843c749SSergey Zigachev {
385b843c749SSergey Zigachev 	struct drm_sched_entity *entity =
386b843c749SSergey Zigachev 		container_of(cb, struct drm_sched_entity, cb);
387b843c749SSergey Zigachev 	entity->dependency = NULL;
388b843c749SSergey Zigachev 	dma_fence_put(f);
389b843c749SSergey Zigachev }
390b843c749SSergey Zigachev 
391b843c749SSergey Zigachev /**
392b843c749SSergey Zigachev  * drm_sched_entity_set_rq - Sets the run queue for an entity
393b843c749SSergey Zigachev  *
394b843c749SSergey Zigachev  * @entity: scheduler entity
395b843c749SSergey Zigachev  * @rq: scheduler run queue
396b843c749SSergey Zigachev  *
397b843c749SSergey Zigachev  * Sets the run queue for an entity and removes the entity from the previous
398b843c749SSergey Zigachev  * run queue in which was present.
399b843c749SSergey Zigachev  */
drm_sched_entity_set_rq(struct drm_sched_entity * entity,struct drm_sched_rq * rq)400b843c749SSergey Zigachev void drm_sched_entity_set_rq(struct drm_sched_entity *entity,
401b843c749SSergey Zigachev 			     struct drm_sched_rq *rq)
402b843c749SSergey Zigachev {
403b843c749SSergey Zigachev 	if (entity->rq == rq)
404b843c749SSergey Zigachev 		return;
405b843c749SSergey Zigachev 
406b843c749SSergey Zigachev 	BUG_ON(!rq);
407b843c749SSergey Zigachev 
408b843c749SSergey Zigachev 	spin_lock(&entity->rq_lock);
409b843c749SSergey Zigachev 	drm_sched_rq_remove_entity(entity->rq, entity);
410b843c749SSergey Zigachev 	entity->rq = rq;
411b843c749SSergey Zigachev 	drm_sched_rq_add_entity(rq, entity);
412b843c749SSergey Zigachev 	spin_unlock(&entity->rq_lock);
413b843c749SSergey Zigachev }
414b843c749SSergey Zigachev EXPORT_SYMBOL(drm_sched_entity_set_rq);
415b843c749SSergey Zigachev 
416b843c749SSergey Zigachev /**
417b843c749SSergey Zigachev  * drm_sched_dependency_optimized
418b843c749SSergey Zigachev  *
419b843c749SSergey Zigachev  * @fence: the dependency fence
420b843c749SSergey Zigachev  * @entity: the entity which depends on the above fence
421b843c749SSergey Zigachev  *
422b843c749SSergey Zigachev  * Returns true if the dependency can be optimized and false otherwise
423b843c749SSergey Zigachev  */
drm_sched_dependency_optimized(struct dma_fence * fence,struct drm_sched_entity * entity)424b843c749SSergey Zigachev bool drm_sched_dependency_optimized(struct dma_fence* fence,
425b843c749SSergey Zigachev 				    struct drm_sched_entity *entity)
426b843c749SSergey Zigachev {
427b843c749SSergey Zigachev 	struct drm_gpu_scheduler *sched = entity->rq->sched;
428b843c749SSergey Zigachev 	struct drm_sched_fence *s_fence;
429b843c749SSergey Zigachev 
430b843c749SSergey Zigachev 	if (!fence || dma_fence_is_signaled(fence))
431b843c749SSergey Zigachev 		return false;
432b843c749SSergey Zigachev 	if (fence->context == entity->fence_context)
433b843c749SSergey Zigachev 		return true;
434b843c749SSergey Zigachev 	s_fence = to_drm_sched_fence(fence);
435b843c749SSergey Zigachev 	if (s_fence && s_fence->sched == sched)
436b843c749SSergey Zigachev 		return true;
437b843c749SSergey Zigachev 
438b843c749SSergey Zigachev 	return false;
439b843c749SSergey Zigachev }
440b843c749SSergey Zigachev EXPORT_SYMBOL(drm_sched_dependency_optimized);
441b843c749SSergey Zigachev 
drm_sched_entity_add_dependency_cb(struct drm_sched_entity * entity)442b843c749SSergey Zigachev static bool drm_sched_entity_add_dependency_cb(struct drm_sched_entity *entity)
443b843c749SSergey Zigachev {
444b843c749SSergey Zigachev 	struct drm_gpu_scheduler *sched = entity->rq->sched;
445b843c749SSergey Zigachev 	struct dma_fence * fence = entity->dependency;
446b843c749SSergey Zigachev 	struct drm_sched_fence *s_fence;
447b843c749SSergey Zigachev 
448b843c749SSergey Zigachev 	if (fence->context == entity->fence_context ||
449b843c749SSergey Zigachev             fence->context == entity->fence_context + 1) {
450b843c749SSergey Zigachev                 /*
451b843c749SSergey Zigachev                  * Fence is a scheduled/finished fence from a job
452b843c749SSergey Zigachev                  * which belongs to the same entity, we can ignore
453b843c749SSergey Zigachev                  * fences from ourself
454b843c749SSergey Zigachev                  */
455b843c749SSergey Zigachev 		dma_fence_put(entity->dependency);
456b843c749SSergey Zigachev 		return false;
457b843c749SSergey Zigachev 	}
458b843c749SSergey Zigachev 
459b843c749SSergey Zigachev 	s_fence = to_drm_sched_fence(fence);
460b843c749SSergey Zigachev 	if (s_fence && s_fence->sched == sched) {
461b843c749SSergey Zigachev 
462b843c749SSergey Zigachev 		/*
463b843c749SSergey Zigachev 		 * Fence is from the same scheduler, only need to wait for
464b843c749SSergey Zigachev 		 * it to be scheduled
465b843c749SSergey Zigachev 		 */
466b843c749SSergey Zigachev 		fence = dma_fence_get(&s_fence->scheduled);
467b843c749SSergey Zigachev 		dma_fence_put(entity->dependency);
468b843c749SSergey Zigachev 		entity->dependency = fence;
469b843c749SSergey Zigachev 		if (!dma_fence_add_callback(fence, &entity->cb,
470b843c749SSergey Zigachev 					    drm_sched_entity_clear_dep))
471b843c749SSergey Zigachev 			return true;
472b843c749SSergey Zigachev 
473b843c749SSergey Zigachev 		/* Ignore it when it is already scheduled */
474b843c749SSergey Zigachev 		dma_fence_put(fence);
475b843c749SSergey Zigachev 		return false;
476b843c749SSergey Zigachev 	}
477b843c749SSergey Zigachev 
478b843c749SSergey Zigachev 	if (!dma_fence_add_callback(entity->dependency, &entity->cb,
479b843c749SSergey Zigachev 				    drm_sched_entity_wakeup))
480b843c749SSergey Zigachev 		return true;
481b843c749SSergey Zigachev 
482b843c749SSergey Zigachev 	dma_fence_put(entity->dependency);
483b843c749SSergey Zigachev 	return false;
484b843c749SSergey Zigachev }
485b843c749SSergey Zigachev 
486b843c749SSergey Zigachev static struct drm_sched_job *
drm_sched_entity_pop_job(struct drm_sched_entity * entity)487b843c749SSergey Zigachev drm_sched_entity_pop_job(struct drm_sched_entity *entity)
488b843c749SSergey Zigachev {
489b843c749SSergey Zigachev 	struct drm_gpu_scheduler *sched = entity->rq->sched;
490b843c749SSergey Zigachev 	struct drm_sched_job *sched_job = to_drm_sched_job(
491b843c749SSergey Zigachev 						spsc_queue_peek(&entity->job_queue));
492b843c749SSergey Zigachev 
493b843c749SSergey Zigachev 	if (!sched_job)
494b843c749SSergey Zigachev 		return NULL;
495b843c749SSergey Zigachev 
496b843c749SSergey Zigachev 	while ((entity->dependency = sched->ops->dependency(sched_job, entity)))
497b843c749SSergey Zigachev 		if (drm_sched_entity_add_dependency_cb(entity))
498b843c749SSergey Zigachev 			return NULL;
499b843c749SSergey Zigachev 
500b843c749SSergey Zigachev 	/* skip jobs from entity that marked guilty */
501b843c749SSergey Zigachev 	if (entity->guilty && atomic_read(entity->guilty))
502b843c749SSergey Zigachev 		dma_fence_set_error(&sched_job->s_fence->finished, -ECANCELED);
503b843c749SSergey Zigachev 
504b843c749SSergey Zigachev 	dma_fence_put(entity->last_scheduled);
505b843c749SSergey Zigachev 	entity->last_scheduled = dma_fence_get(&sched_job->s_fence->finished);
506b843c749SSergey Zigachev 
507b843c749SSergey Zigachev 	spsc_queue_pop(&entity->job_queue);
508b843c749SSergey Zigachev 	return sched_job;
509b843c749SSergey Zigachev }
510b843c749SSergey Zigachev 
511b843c749SSergey Zigachev /**
512b843c749SSergey Zigachev  * drm_sched_entity_push_job - Submit a job to the entity's job queue
513b843c749SSergey Zigachev  *
514b843c749SSergey Zigachev  * @sched_job: job to submit
515b843c749SSergey Zigachev  * @entity: scheduler entity
516b843c749SSergey Zigachev  *
517b843c749SSergey Zigachev  * Note: To guarantee that the order of insertion to queue matches
518b843c749SSergey Zigachev  * the job's fence sequence number this function should be
519b843c749SSergey Zigachev  * called with drm_sched_job_init under common lock.
520b843c749SSergey Zigachev  *
521b843c749SSergey Zigachev  * Returns 0 for success, negative error code otherwise.
522b843c749SSergey Zigachev  */
drm_sched_entity_push_job(struct drm_sched_job * sched_job,struct drm_sched_entity * entity)523b843c749SSergey Zigachev void drm_sched_entity_push_job(struct drm_sched_job *sched_job,
524b843c749SSergey Zigachev 			       struct drm_sched_entity *entity)
525b843c749SSergey Zigachev {
526b843c749SSergey Zigachev 	struct drm_gpu_scheduler *sched = sched_job->sched;
527b843c749SSergey Zigachev 	bool first = false;
528b843c749SSergey Zigachev 
529*78973132SSergey Zigachev #if 0
530b843c749SSergey Zigachev 	trace_drm_sched_job(sched_job, entity);
531*78973132SSergey Zigachev #endif
532b843c749SSergey Zigachev 
533*78973132SSergey Zigachev #if 0
534b843c749SSergey Zigachev 	WRITE_ONCE(entity->last_user, current->group_leader);
535*78973132SSergey Zigachev #endif
536b843c749SSergey Zigachev 	first = spsc_queue_push(&entity->job_queue, &sched_job->queue_node);
537b843c749SSergey Zigachev 
538b843c749SSergey Zigachev 	/* first job wakes up scheduler */
539b843c749SSergey Zigachev 	if (first) {
540b843c749SSergey Zigachev 		/* Add the entity to the run queue */
541b843c749SSergey Zigachev 		spin_lock(&entity->rq_lock);
542b843c749SSergey Zigachev 		if (!entity->rq) {
543b843c749SSergey Zigachev 			DRM_ERROR("Trying to push to a killed entity\n");
544b843c749SSergey Zigachev 			spin_unlock(&entity->rq_lock);
545b843c749SSergey Zigachev 			return;
546b843c749SSergey Zigachev 		}
547b843c749SSergey Zigachev 		drm_sched_rq_add_entity(entity->rq, entity);
548b843c749SSergey Zigachev 		spin_unlock(&entity->rq_lock);
549b843c749SSergey Zigachev 		drm_sched_wakeup(sched);
550b843c749SSergey Zigachev 	}
551b843c749SSergey Zigachev }
552b843c749SSergey Zigachev EXPORT_SYMBOL(drm_sched_entity_push_job);
553b843c749SSergey Zigachev 
554b843c749SSergey Zigachev /* job_finish is called after hw fence signaled
555b843c749SSergey Zigachev  */
drm_sched_job_finish(struct work_struct * work)556b843c749SSergey Zigachev static void drm_sched_job_finish(struct work_struct *work)
557b843c749SSergey Zigachev {
558b843c749SSergey Zigachev 	struct drm_sched_job *s_job = container_of(work, struct drm_sched_job,
559b843c749SSergey Zigachev 						   finish_work);
560b843c749SSergey Zigachev 	struct drm_gpu_scheduler *sched = s_job->sched;
561b843c749SSergey Zigachev 
562b843c749SSergey Zigachev 	/*
563b843c749SSergey Zigachev 	 * Canceling the timeout without removing our job from the ring mirror
564b843c749SSergey Zigachev 	 * list is safe, as we will only end up in this worker if our jobs
565b843c749SSergey Zigachev 	 * finished fence has been signaled. So even if some another worker
566b843c749SSergey Zigachev 	 * manages to find this job as the next job in the list, the fence
567b843c749SSergey Zigachev 	 * signaled check below will prevent the timeout to be restarted.
568b843c749SSergey Zigachev 	 */
569b843c749SSergey Zigachev 	cancel_delayed_work_sync(&s_job->work_tdr);
570b843c749SSergey Zigachev 
571b843c749SSergey Zigachev 	spin_lock(&sched->job_list_lock);
572b843c749SSergey Zigachev 	/* queue TDR for next job */
573b843c749SSergey Zigachev 	if (sched->timeout != MAX_SCHEDULE_TIMEOUT &&
574b843c749SSergey Zigachev 	    !list_is_last(&s_job->node, &sched->ring_mirror_list)) {
575b843c749SSergey Zigachev 		struct drm_sched_job *next = list_next_entry(s_job, node);
576b843c749SSergey Zigachev 
577b843c749SSergey Zigachev 		if (!dma_fence_is_signaled(&next->s_fence->finished))
578b843c749SSergey Zigachev 			schedule_delayed_work(&next->work_tdr, sched->timeout);
579b843c749SSergey Zigachev 	}
580b843c749SSergey Zigachev 	/* remove job from ring_mirror_list */
581b843c749SSergey Zigachev 	list_del(&s_job->node);
582b843c749SSergey Zigachev 	spin_unlock(&sched->job_list_lock);
583b843c749SSergey Zigachev 
584b843c749SSergey Zigachev 	dma_fence_put(&s_job->s_fence->finished);
585b843c749SSergey Zigachev 	sched->ops->free_job(s_job);
586b843c749SSergey Zigachev }
587b843c749SSergey Zigachev 
drm_sched_job_finish_cb(struct dma_fence * f,struct dma_fence_cb * cb)588b843c749SSergey Zigachev static void drm_sched_job_finish_cb(struct dma_fence *f,
589b843c749SSergey Zigachev 				    struct dma_fence_cb *cb)
590b843c749SSergey Zigachev {
591b843c749SSergey Zigachev 	struct drm_sched_job *job = container_of(cb, struct drm_sched_job,
592b843c749SSergey Zigachev 						 finish_cb);
593b843c749SSergey Zigachev 	schedule_work(&job->finish_work);
594b843c749SSergey Zigachev }
595b843c749SSergey Zigachev 
drm_sched_job_begin(struct drm_sched_job * s_job)596b843c749SSergey Zigachev static void drm_sched_job_begin(struct drm_sched_job *s_job)
597b843c749SSergey Zigachev {
598b843c749SSergey Zigachev 	struct drm_gpu_scheduler *sched = s_job->sched;
599b843c749SSergey Zigachev 
600b843c749SSergey Zigachev 	dma_fence_add_callback(&s_job->s_fence->finished, &s_job->finish_cb,
601b843c749SSergey Zigachev 			       drm_sched_job_finish_cb);
602b843c749SSergey Zigachev 
603b843c749SSergey Zigachev 	spin_lock(&sched->job_list_lock);
604b843c749SSergey Zigachev 	list_add_tail(&s_job->node, &sched->ring_mirror_list);
605b843c749SSergey Zigachev 	if (sched->timeout != MAX_SCHEDULE_TIMEOUT &&
606b843c749SSergey Zigachev 	    list_first_entry_or_null(&sched->ring_mirror_list,
607b843c749SSergey Zigachev 				     struct drm_sched_job, node) == s_job)
608b843c749SSergey Zigachev 		schedule_delayed_work(&s_job->work_tdr, sched->timeout);
609b843c749SSergey Zigachev 	spin_unlock(&sched->job_list_lock);
610b843c749SSergey Zigachev }
611b843c749SSergey Zigachev 
drm_sched_job_timedout(struct work_struct * work)612b843c749SSergey Zigachev static void drm_sched_job_timedout(struct work_struct *work)
613b843c749SSergey Zigachev {
614b843c749SSergey Zigachev 	struct drm_sched_job *job = container_of(work, struct drm_sched_job,
615b843c749SSergey Zigachev 						 work_tdr.work);
616b843c749SSergey Zigachev 
617b843c749SSergey Zigachev 	job->sched->ops->timedout_job(job);
618b843c749SSergey Zigachev }
619b843c749SSergey Zigachev 
620b843c749SSergey Zigachev /**
621b843c749SSergey Zigachev  * drm_sched_hw_job_reset - stop the scheduler if it contains the bad job
622b843c749SSergey Zigachev  *
623b843c749SSergey Zigachev  * @sched: scheduler instance
624b843c749SSergey Zigachev  * @bad: bad scheduler job
625b843c749SSergey Zigachev  *
626b843c749SSergey Zigachev  */
drm_sched_hw_job_reset(struct drm_gpu_scheduler * sched,struct drm_sched_job * bad)627b843c749SSergey Zigachev void drm_sched_hw_job_reset(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad)
628b843c749SSergey Zigachev {
629b843c749SSergey Zigachev 	struct drm_sched_job *s_job;
630b843c749SSergey Zigachev 	struct drm_sched_entity *entity, *tmp;
631b843c749SSergey Zigachev 	int i;
632b843c749SSergey Zigachev 
633b843c749SSergey Zigachev 	spin_lock(&sched->job_list_lock);
634b843c749SSergey Zigachev 	list_for_each_entry_reverse(s_job, &sched->ring_mirror_list, node) {
635b843c749SSergey Zigachev 		if (s_job->s_fence->parent &&
636b843c749SSergey Zigachev 		    dma_fence_remove_callback(s_job->s_fence->parent,
637b843c749SSergey Zigachev 					      &s_job->s_fence->cb)) {
638b843c749SSergey Zigachev 			dma_fence_put(s_job->s_fence->parent);
639b843c749SSergey Zigachev 			s_job->s_fence->parent = NULL;
640b843c749SSergey Zigachev 			atomic_dec(&sched->hw_rq_count);
641b843c749SSergey Zigachev 		}
642b843c749SSergey Zigachev 	}
643b843c749SSergey Zigachev 	spin_unlock(&sched->job_list_lock);
644b843c749SSergey Zigachev 
645b843c749SSergey Zigachev 	if (bad && bad->s_priority != DRM_SCHED_PRIORITY_KERNEL) {
646b843c749SSergey Zigachev 		atomic_inc(&bad->karma);
647b843c749SSergey Zigachev 		/* don't increase @bad's karma if it's from KERNEL RQ,
648b843c749SSergey Zigachev 		 * becuase sometimes GPU hang would cause kernel jobs (like VM updating jobs)
649b843c749SSergey Zigachev 		 * corrupt but keep in mind that kernel jobs always considered good.
650b843c749SSergey Zigachev 		 */
651b843c749SSergey Zigachev 		for (i = DRM_SCHED_PRIORITY_MIN; i < DRM_SCHED_PRIORITY_KERNEL; i++ ) {
652b843c749SSergey Zigachev 			struct drm_sched_rq *rq = &sched->sched_rq[i];
653b843c749SSergey Zigachev 
654b843c749SSergey Zigachev 			spin_lock(&rq->lock);
655b843c749SSergey Zigachev 			list_for_each_entry_safe(entity, tmp, &rq->entities, list) {
656b843c749SSergey Zigachev 				if (bad->s_fence->scheduled.context == entity->fence_context) {
657b843c749SSergey Zigachev 				    if (atomic_read(&bad->karma) > bad->sched->hang_limit)
658b843c749SSergey Zigachev 						if (entity->guilty)
659b843c749SSergey Zigachev 							atomic_set(entity->guilty, 1);
660b843c749SSergey Zigachev 					break;
661b843c749SSergey Zigachev 				}
662b843c749SSergey Zigachev 			}
663b843c749SSergey Zigachev 			spin_unlock(&rq->lock);
664b843c749SSergey Zigachev 			if (&entity->list != &rq->entities)
665b843c749SSergey Zigachev 				break;
666b843c749SSergey Zigachev 		}
667b843c749SSergey Zigachev 	}
668b843c749SSergey Zigachev }
669b843c749SSergey Zigachev EXPORT_SYMBOL(drm_sched_hw_job_reset);
670b843c749SSergey Zigachev 
671b843c749SSergey Zigachev /**
672b843c749SSergey Zigachev  * drm_sched_job_recovery - recover jobs after a reset
673b843c749SSergey Zigachev  *
674b843c749SSergey Zigachev  * @sched: scheduler instance
675b843c749SSergey Zigachev  *
676b843c749SSergey Zigachev  */
drm_sched_job_recovery(struct drm_gpu_scheduler * sched)677b843c749SSergey Zigachev void drm_sched_job_recovery(struct drm_gpu_scheduler *sched)
678b843c749SSergey Zigachev {
679b843c749SSergey Zigachev 	struct drm_sched_job *s_job, *tmp;
680b843c749SSergey Zigachev 	bool found_guilty = false;
681b843c749SSergey Zigachev 	int r;
682b843c749SSergey Zigachev 
683b843c749SSergey Zigachev 	spin_lock(&sched->job_list_lock);
684b843c749SSergey Zigachev 	s_job = list_first_entry_or_null(&sched->ring_mirror_list,
685b843c749SSergey Zigachev 					 struct drm_sched_job, node);
686b843c749SSergey Zigachev 	if (s_job && sched->timeout != MAX_SCHEDULE_TIMEOUT)
687b843c749SSergey Zigachev 		schedule_delayed_work(&s_job->work_tdr, sched->timeout);
688b843c749SSergey Zigachev 
689b843c749SSergey Zigachev 	list_for_each_entry_safe(s_job, tmp, &sched->ring_mirror_list, node) {
690b843c749SSergey Zigachev 		struct drm_sched_fence *s_fence = s_job->s_fence;
691b843c749SSergey Zigachev 		struct dma_fence *fence;
692b843c749SSergey Zigachev 		uint64_t guilty_context;
693b843c749SSergey Zigachev 
694b843c749SSergey Zigachev 		if (!found_guilty && atomic_read(&s_job->karma) > sched->hang_limit) {
695b843c749SSergey Zigachev 			found_guilty = true;
696b843c749SSergey Zigachev 			guilty_context = s_job->s_fence->scheduled.context;
697b843c749SSergey Zigachev 		}
698b843c749SSergey Zigachev 
699b843c749SSergey Zigachev 		if (found_guilty && s_job->s_fence->scheduled.context == guilty_context)
700b843c749SSergey Zigachev 			dma_fence_set_error(&s_fence->finished, -ECANCELED);
701b843c749SSergey Zigachev 
702b843c749SSergey Zigachev 		spin_unlock(&sched->job_list_lock);
703b843c749SSergey Zigachev 		fence = sched->ops->run_job(s_job);
704b843c749SSergey Zigachev 		atomic_inc(&sched->hw_rq_count);
705b843c749SSergey Zigachev 
706b843c749SSergey Zigachev 		if (fence) {
707b843c749SSergey Zigachev 			s_fence->parent = dma_fence_get(fence);
708b843c749SSergey Zigachev 			r = dma_fence_add_callback(fence, &s_fence->cb,
709b843c749SSergey Zigachev 						   drm_sched_process_job);
710b843c749SSergey Zigachev 			if (r == -ENOENT)
711b843c749SSergey Zigachev 				drm_sched_process_job(fence, &s_fence->cb);
712b843c749SSergey Zigachev 			else if (r)
713b843c749SSergey Zigachev 				DRM_ERROR("fence add callback failed (%d)\n",
714b843c749SSergey Zigachev 					  r);
715b843c749SSergey Zigachev 			dma_fence_put(fence);
716b843c749SSergey Zigachev 		} else {
717b843c749SSergey Zigachev 			drm_sched_process_job(NULL, &s_fence->cb);
718b843c749SSergey Zigachev 		}
719b843c749SSergey Zigachev 		spin_lock(&sched->job_list_lock);
720b843c749SSergey Zigachev 	}
721b843c749SSergey Zigachev 	spin_unlock(&sched->job_list_lock);
722b843c749SSergey Zigachev }
723b843c749SSergey Zigachev EXPORT_SYMBOL(drm_sched_job_recovery);
724b843c749SSergey Zigachev 
725b843c749SSergey Zigachev /**
726b843c749SSergey Zigachev  * drm_sched_job_init - init a scheduler job
727b843c749SSergey Zigachev  *
728b843c749SSergey Zigachev  * @job: scheduler job to init
729b843c749SSergey Zigachev  * @entity: scheduler entity to use
730b843c749SSergey Zigachev  * @owner: job owner for debugging
731b843c749SSergey Zigachev  *
732b843c749SSergey Zigachev  * Refer to drm_sched_entity_push_job() documentation
733b843c749SSergey Zigachev  * for locking considerations.
734b843c749SSergey Zigachev  *
735b843c749SSergey Zigachev  * Returns 0 for success, negative error code otherwise.
736b843c749SSergey Zigachev  */
drm_sched_job_init(struct drm_sched_job * job,struct drm_sched_entity * entity,void * owner)737b843c749SSergey Zigachev int drm_sched_job_init(struct drm_sched_job *job,
738b843c749SSergey Zigachev 		       struct drm_sched_entity *entity,
739b843c749SSergey Zigachev 		       void *owner)
740b843c749SSergey Zigachev {
741b843c749SSergey Zigachev 	struct drm_gpu_scheduler *sched = entity->rq->sched;
742b843c749SSergey Zigachev 
743b843c749SSergey Zigachev 	job->sched = sched;
744b843c749SSergey Zigachev 	job->entity = entity;
745b843c749SSergey Zigachev 	job->s_priority = entity->rq - sched->sched_rq;
746b843c749SSergey Zigachev 	job->s_fence = drm_sched_fence_create(entity, owner);
747b843c749SSergey Zigachev 	if (!job->s_fence)
748b843c749SSergey Zigachev 		return -ENOMEM;
749b843c749SSergey Zigachev 	job->id = atomic64_inc_return(&sched->job_id_count);
750b843c749SSergey Zigachev 
751b843c749SSergey Zigachev 	INIT_WORK(&job->finish_work, drm_sched_job_finish);
752b843c749SSergey Zigachev 	INIT_LIST_HEAD(&job->node);
753b843c749SSergey Zigachev 	INIT_DELAYED_WORK(&job->work_tdr, drm_sched_job_timedout);
754b843c749SSergey Zigachev 
755b843c749SSergey Zigachev 	return 0;
756b843c749SSergey Zigachev }
757b843c749SSergey Zigachev EXPORT_SYMBOL(drm_sched_job_init);
758b843c749SSergey Zigachev 
759b843c749SSergey Zigachev /**
760b843c749SSergey Zigachev  * drm_sched_ready - is the scheduler ready
761b843c749SSergey Zigachev  *
762b843c749SSergey Zigachev  * @sched: scheduler instance
763b843c749SSergey Zigachev  *
764b843c749SSergey Zigachev  * Return true if we can push more jobs to the hw, otherwise false.
765b843c749SSergey Zigachev  */
drm_sched_ready(struct drm_gpu_scheduler * sched)766b843c749SSergey Zigachev static bool drm_sched_ready(struct drm_gpu_scheduler *sched)
767b843c749SSergey Zigachev {
768b843c749SSergey Zigachev 	return atomic_read(&sched->hw_rq_count) <
769b843c749SSergey Zigachev 		sched->hw_submission_limit;
770b843c749SSergey Zigachev }
771b843c749SSergey Zigachev 
772b843c749SSergey Zigachev /**
773b843c749SSergey Zigachev  * drm_sched_wakeup - Wake up the scheduler when it is ready
774b843c749SSergey Zigachev  *
775b843c749SSergey Zigachev  * @sched: scheduler instance
776b843c749SSergey Zigachev  *
777b843c749SSergey Zigachev  */
drm_sched_wakeup(struct drm_gpu_scheduler * sched)778b843c749SSergey Zigachev static void drm_sched_wakeup(struct drm_gpu_scheduler *sched)
779b843c749SSergey Zigachev {
780b843c749SSergey Zigachev 	if (drm_sched_ready(sched))
781b843c749SSergey Zigachev 		wake_up_interruptible(&sched->wake_up_worker);
782b843c749SSergey Zigachev }
783b843c749SSergey Zigachev 
784b843c749SSergey Zigachev /**
785b843c749SSergey Zigachev  * drm_sched_select_entity - Select next entity to process
786b843c749SSergey Zigachev  *
787b843c749SSergey Zigachev  * @sched: scheduler instance
788b843c749SSergey Zigachev  *
789b843c749SSergey Zigachev  * Returns the entity to process or NULL if none are found.
790b843c749SSergey Zigachev  */
791b843c749SSergey Zigachev static struct drm_sched_entity *
drm_sched_select_entity(struct drm_gpu_scheduler * sched)792b843c749SSergey Zigachev drm_sched_select_entity(struct drm_gpu_scheduler *sched)
793b843c749SSergey Zigachev {
794b843c749SSergey Zigachev 	struct drm_sched_entity *entity;
795b843c749SSergey Zigachev 	int i;
796b843c749SSergey Zigachev 
797b843c749SSergey Zigachev 	if (!drm_sched_ready(sched))
798b843c749SSergey Zigachev 		return NULL;
799b843c749SSergey Zigachev 
800b843c749SSergey Zigachev 	/* Kernel run queue has higher priority than normal run queue*/
801b843c749SSergey Zigachev 	for (i = DRM_SCHED_PRIORITY_MAX - 1; i >= DRM_SCHED_PRIORITY_MIN; i--) {
802b843c749SSergey Zigachev 		entity = drm_sched_rq_select_entity(&sched->sched_rq[i]);
803b843c749SSergey Zigachev 		if (entity)
804b843c749SSergey Zigachev 			break;
805b843c749SSergey Zigachev 	}
806b843c749SSergey Zigachev 
807b843c749SSergey Zigachev 	return entity;
808b843c749SSergey Zigachev }
809b843c749SSergey Zigachev 
810b843c749SSergey Zigachev /**
811b843c749SSergey Zigachev  * drm_sched_process_job - process a job
812b843c749SSergey Zigachev  *
813b843c749SSergey Zigachev  * @f: fence
814b843c749SSergey Zigachev  * @cb: fence callbacks
815b843c749SSergey Zigachev  *
816b843c749SSergey Zigachev  * Called after job has finished execution.
817b843c749SSergey Zigachev  */
drm_sched_process_job(struct dma_fence * f,struct dma_fence_cb * cb)818b843c749SSergey Zigachev static void drm_sched_process_job(struct dma_fence *f, struct dma_fence_cb *cb)
819b843c749SSergey Zigachev {
820b843c749SSergey Zigachev 	struct drm_sched_fence *s_fence =
821b843c749SSergey Zigachev 		container_of(cb, struct drm_sched_fence, cb);
822b843c749SSergey Zigachev 	struct drm_gpu_scheduler *sched = s_fence->sched;
823b843c749SSergey Zigachev 
824b843c749SSergey Zigachev 	dma_fence_get(&s_fence->finished);
825b843c749SSergey Zigachev 	atomic_dec(&sched->hw_rq_count);
826b843c749SSergey Zigachev 	drm_sched_fence_finished(s_fence);
827b843c749SSergey Zigachev 
828*78973132SSergey Zigachev #if 0
829b843c749SSergey Zigachev 	trace_drm_sched_process_job(s_fence);
830*78973132SSergey Zigachev #endif
831b843c749SSergey Zigachev 	dma_fence_put(&s_fence->finished);
832b843c749SSergey Zigachev 	wake_up_interruptible(&sched->wake_up_worker);
833b843c749SSergey Zigachev }
834b843c749SSergey Zigachev 
835b843c749SSergey Zigachev /**
836b843c749SSergey Zigachev  * drm_sched_blocked - check if the scheduler is blocked
837b843c749SSergey Zigachev  *
838b843c749SSergey Zigachev  * @sched: scheduler instance
839b843c749SSergey Zigachev  *
840b843c749SSergey Zigachev  * Returns true if blocked, otherwise false.
841b843c749SSergey Zigachev  */
drm_sched_blocked(struct drm_gpu_scheduler * sched)842b843c749SSergey Zigachev static bool drm_sched_blocked(struct drm_gpu_scheduler *sched)
843b843c749SSergey Zigachev {
844b843c749SSergey Zigachev 	if (kthread_should_park()) {
845b843c749SSergey Zigachev 		kthread_parkme();
846b843c749SSergey Zigachev 		return true;
847b843c749SSergey Zigachev 	}
848b843c749SSergey Zigachev 
849b843c749SSergey Zigachev 	return false;
850b843c749SSergey Zigachev }
851b843c749SSergey Zigachev 
852b843c749SSergey Zigachev /**
853b843c749SSergey Zigachev  * drm_sched_main - main scheduler thread
854b843c749SSergey Zigachev  *
855b843c749SSergey Zigachev  * @param: scheduler instance
856b843c749SSergey Zigachev  *
857b843c749SSergey Zigachev  * Returns 0.
858b843c749SSergey Zigachev  */
drm_sched_main(void * param)859b843c749SSergey Zigachev static int drm_sched_main(void *param)
860b843c749SSergey Zigachev {
861*78973132SSergey Zigachev #if 0
862b843c749SSergey Zigachev 	struct sched_param sparam = {.sched_priority = 1};
863*78973132SSergey Zigachev #endif
864b843c749SSergey Zigachev 	struct drm_gpu_scheduler *sched = (struct drm_gpu_scheduler *)param;
865b843c749SSergey Zigachev 	int r;
866b843c749SSergey Zigachev 
867*78973132SSergey Zigachev #if 0
868b843c749SSergey Zigachev 	sched_setscheduler(current, SCHED_FIFO, &sparam);
869*78973132SSergey Zigachev #endif
870b843c749SSergey Zigachev 
871b843c749SSergey Zigachev 	while (!kthread_should_stop()) {
872b843c749SSergey Zigachev 		struct drm_sched_entity *entity = NULL;
873b843c749SSergey Zigachev 		struct drm_sched_fence *s_fence;
874b843c749SSergey Zigachev 		struct drm_sched_job *sched_job;
875b843c749SSergey Zigachev 		struct dma_fence *fence;
876b843c749SSergey Zigachev 
877b843c749SSergey Zigachev 		wait_event_interruptible(sched->wake_up_worker,
878b843c749SSergey Zigachev 					 (!drm_sched_blocked(sched) &&
879b843c749SSergey Zigachev 					  (entity = drm_sched_select_entity(sched))) ||
880b843c749SSergey Zigachev 					 kthread_should_stop());
881b843c749SSergey Zigachev 
882b843c749SSergey Zigachev 		if (!entity)
883b843c749SSergey Zigachev 			continue;
884b843c749SSergey Zigachev 
885b843c749SSergey Zigachev 		sched_job = drm_sched_entity_pop_job(entity);
886b843c749SSergey Zigachev 		if (!sched_job)
887b843c749SSergey Zigachev 			continue;
888b843c749SSergey Zigachev 
889b843c749SSergey Zigachev 		s_fence = sched_job->s_fence;
890b843c749SSergey Zigachev 
891b843c749SSergey Zigachev 		atomic_inc(&sched->hw_rq_count);
892b843c749SSergey Zigachev 		drm_sched_job_begin(sched_job);
893b843c749SSergey Zigachev 
894b843c749SSergey Zigachev 		fence = sched->ops->run_job(sched_job);
895b843c749SSergey Zigachev 		drm_sched_fence_scheduled(s_fence);
896b843c749SSergey Zigachev 
897b843c749SSergey Zigachev 		if (fence) {
898b843c749SSergey Zigachev 			s_fence->parent = dma_fence_get(fence);
899b843c749SSergey Zigachev 			r = dma_fence_add_callback(fence, &s_fence->cb,
900b843c749SSergey Zigachev 						   drm_sched_process_job);
901b843c749SSergey Zigachev 			if (r == -ENOENT)
902b843c749SSergey Zigachev 				drm_sched_process_job(fence, &s_fence->cb);
903b843c749SSergey Zigachev 			else if (r)
904b843c749SSergey Zigachev 				DRM_ERROR("fence add callback failed (%d)\n",
905b843c749SSergey Zigachev 					  r);
906b843c749SSergey Zigachev 			dma_fence_put(fence);
907b843c749SSergey Zigachev 		} else {
908b843c749SSergey Zigachev 			drm_sched_process_job(NULL, &s_fence->cb);
909b843c749SSergey Zigachev 		}
910b843c749SSergey Zigachev 
911b843c749SSergey Zigachev 		wake_up(&sched->job_scheduled);
912b843c749SSergey Zigachev 	}
913b843c749SSergey Zigachev 	return 0;
914b843c749SSergey Zigachev }
915b843c749SSergey Zigachev 
916b843c749SSergey Zigachev /**
917b843c749SSergey Zigachev  * drm_sched_init - Init a gpu scheduler instance
918b843c749SSergey Zigachev  *
919b843c749SSergey Zigachev  * @sched: scheduler instance
920b843c749SSergey Zigachev  * @ops: backend operations for this scheduler
921b843c749SSergey Zigachev  * @hw_submission: number of hw submissions that can be in flight
922b843c749SSergey Zigachev  * @hang_limit: number of times to allow a job to hang before dropping it
923b843c749SSergey Zigachev  * @timeout: timeout value in jiffies for the scheduler
924b843c749SSergey Zigachev  * @name: name used for debugging
925b843c749SSergey Zigachev  *
926b843c749SSergey Zigachev  * Return 0 on success, otherwise error code.
927b843c749SSergey Zigachev  */
drm_sched_init(struct drm_gpu_scheduler * sched,const struct drm_sched_backend_ops * ops,unsigned hw_submission,unsigned hang_limit,long timeout,const char * name)928b843c749SSergey Zigachev int drm_sched_init(struct drm_gpu_scheduler *sched,
929b843c749SSergey Zigachev 		   const struct drm_sched_backend_ops *ops,
930b843c749SSergey Zigachev 		   unsigned hw_submission,
931b843c749SSergey Zigachev 		   unsigned hang_limit,
932b843c749SSergey Zigachev 		   long timeout,
933b843c749SSergey Zigachev 		   const char *name)
934b843c749SSergey Zigachev {
935b843c749SSergey Zigachev 	int i;
936b843c749SSergey Zigachev 	sched->ops = ops;
937b843c749SSergey Zigachev 	sched->hw_submission_limit = hw_submission;
938b843c749SSergey Zigachev 	sched->name = name;
939b843c749SSergey Zigachev 	sched->timeout = timeout;
940b843c749SSergey Zigachev 	sched->hang_limit = hang_limit;
941b843c749SSergey Zigachev 	for (i = DRM_SCHED_PRIORITY_MIN; i < DRM_SCHED_PRIORITY_MAX; i++)
942b843c749SSergey Zigachev 		drm_sched_rq_init(sched, &sched->sched_rq[i]);
943b843c749SSergey Zigachev 
944b843c749SSergey Zigachev 	init_waitqueue_head(&sched->wake_up_worker);
945b843c749SSergey Zigachev 	init_waitqueue_head(&sched->job_scheduled);
946b843c749SSergey Zigachev 	INIT_LIST_HEAD(&sched->ring_mirror_list);
947*78973132SSergey Zigachev 	spin_init(&sched->job_list_lock, "dgsjll");
948b843c749SSergey Zigachev 	atomic_set(&sched->hw_rq_count, 0);
949b843c749SSergey Zigachev 	atomic64_set(&sched->job_id_count, 0);
950b843c749SSergey Zigachev 
951b843c749SSergey Zigachev 	/* Each scheduler will run on a seperate kernel thread */
952b843c749SSergey Zigachev 	sched->thread = kthread_run(drm_sched_main, sched, sched->name);
953b843c749SSergey Zigachev 	if (IS_ERR(sched->thread)) {
954b843c749SSergey Zigachev 		DRM_ERROR("Failed to create scheduler for %s.\n", name);
955b843c749SSergey Zigachev 		return PTR_ERR(sched->thread);
956b843c749SSergey Zigachev 	}
957b843c749SSergey Zigachev 
958b843c749SSergey Zigachev 	return 0;
959b843c749SSergey Zigachev }
960b843c749SSergey Zigachev EXPORT_SYMBOL(drm_sched_init);
961b843c749SSergey Zigachev 
962b843c749SSergey Zigachev /**
963b843c749SSergey Zigachev  * drm_sched_fini - Destroy a gpu scheduler
964b843c749SSergey Zigachev  *
965b843c749SSergey Zigachev  * @sched: scheduler instance
966b843c749SSergey Zigachev  *
967b843c749SSergey Zigachev  * Tears down and cleans up the scheduler.
968b843c749SSergey Zigachev  */
drm_sched_fini(struct drm_gpu_scheduler * sched)969b843c749SSergey Zigachev void drm_sched_fini(struct drm_gpu_scheduler *sched)
970b843c749SSergey Zigachev {
971b843c749SSergey Zigachev 	if (sched->thread)
972b843c749SSergey Zigachev 		kthread_stop(sched->thread);
973b843c749SSergey Zigachev }
974b843c749SSergey Zigachev EXPORT_SYMBOL(drm_sched_fini);
975