xref: /openbsd/sys/dev/pci/drm/scheduler/sched_entity.c (revision b9c19e87)
1c349dbc7Sjsg /*
2c349dbc7Sjsg  * Copyright 2015 Advanced Micro Devices, Inc.
3c349dbc7Sjsg  *
4c349dbc7Sjsg  * Permission is hereby granted, free of charge, to any person obtaining a
5c349dbc7Sjsg  * copy of this software and associated documentation files (the "Software"),
6c349dbc7Sjsg  * to deal in the Software without restriction, including without limitation
7c349dbc7Sjsg  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8c349dbc7Sjsg  * and/or sell copies of the Software, and to permit persons to whom the
9c349dbc7Sjsg  * Software is furnished to do so, subject to the following conditions:
10c349dbc7Sjsg  *
11c349dbc7Sjsg  * The above copyright notice and this permission notice shall be included in
12c349dbc7Sjsg  * all copies or substantial portions of the Software.
13c349dbc7Sjsg  *
14c349dbc7Sjsg  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15c349dbc7Sjsg  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16c349dbc7Sjsg  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17c349dbc7Sjsg  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18c349dbc7Sjsg  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19c349dbc7Sjsg  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20c349dbc7Sjsg  * OTHER DEALINGS IN THE SOFTWARE.
21c349dbc7Sjsg  *
22c349dbc7Sjsg  */
23c349dbc7Sjsg 
24c349dbc7Sjsg #include <linux/kthread.h>
25c349dbc7Sjsg #include <linux/slab.h>
26c349dbc7Sjsg #include <linux/completion.h>
27c349dbc7Sjsg 
28c349dbc7Sjsg #include <drm/drm_print.h>
29c349dbc7Sjsg #include <drm/gpu_scheduler.h>
30c349dbc7Sjsg 
31c349dbc7Sjsg #include "gpu_scheduler_trace.h"
32c349dbc7Sjsg 
33c349dbc7Sjsg #define to_drm_sched_job(sched_job)		\
34c349dbc7Sjsg 		container_of((sched_job), struct drm_sched_job, queue_node)
35c349dbc7Sjsg 
36c349dbc7Sjsg /**
37c349dbc7Sjsg  * drm_sched_entity_init - Init a context entity used by scheduler when
38c349dbc7Sjsg  * submit to HW ring.
39c349dbc7Sjsg  *
40c349dbc7Sjsg  * @entity: scheduler entity to init
41c349dbc7Sjsg  * @priority: priority of the entity
42c349dbc7Sjsg  * @sched_list: the list of drm scheds on which jobs from this
43c349dbc7Sjsg  *           entity can be submitted
44c349dbc7Sjsg  * @num_sched_list: number of drm sched in sched_list
45c349dbc7Sjsg  * @guilty: atomic_t set to 1 when a job on this queue
46c349dbc7Sjsg  *          is found to be guilty causing a timeout
47c349dbc7Sjsg  *
481bb76ff1Sjsg  * Note that the &sched_list must have at least one element to schedule the entity.
491bb76ff1Sjsg  *
501bb76ff1Sjsg  * For changing @priority later on at runtime see
511bb76ff1Sjsg  * drm_sched_entity_set_priority(). For changing the set of schedulers
521bb76ff1Sjsg  * @sched_list at runtime see drm_sched_entity_modify_sched().
531bb76ff1Sjsg  *
541bb76ff1Sjsg  * An entity is cleaned up by callind drm_sched_entity_fini(). See also
551bb76ff1Sjsg  * drm_sched_entity_destroy().
56c349dbc7Sjsg  *
57c349dbc7Sjsg  * Returns 0 on success or a negative error code on failure.
58c349dbc7Sjsg  */
drm_sched_entity_init(struct drm_sched_entity * entity,enum drm_sched_priority priority,struct drm_gpu_scheduler ** sched_list,unsigned int num_sched_list,atomic_t * guilty)59c349dbc7Sjsg int drm_sched_entity_init(struct drm_sched_entity *entity,
60c349dbc7Sjsg 			  enum drm_sched_priority priority,
61c349dbc7Sjsg 			  struct drm_gpu_scheduler **sched_list,
62c349dbc7Sjsg 			  unsigned int num_sched_list,
63c349dbc7Sjsg 			  atomic_t *guilty)
64c349dbc7Sjsg {
65c349dbc7Sjsg 	if (!(entity && sched_list && (num_sched_list == 0 || sched_list[0])))
66c349dbc7Sjsg 		return -EINVAL;
67c349dbc7Sjsg 
68c349dbc7Sjsg 	memset(entity, 0, sizeof(struct drm_sched_entity));
69c349dbc7Sjsg 	INIT_LIST_HEAD(&entity->list);
70c349dbc7Sjsg 	entity->rq = NULL;
71c349dbc7Sjsg 	entity->guilty = guilty;
72c349dbc7Sjsg 	entity->num_sched_list = num_sched_list;
73c349dbc7Sjsg 	entity->priority = priority;
74c349dbc7Sjsg 	entity->sched_list = num_sched_list > 1 ? sched_list : NULL;
75f005ef32Sjsg 	RCU_INIT_POINTER(entity->last_scheduled, NULL);
76f005ef32Sjsg 	RB_CLEAR_NODE(&entity->rb_tree_node);
77c349dbc7Sjsg 
78c349dbc7Sjsg 	if(num_sched_list)
79c349dbc7Sjsg 		entity->rq = &sched_list[0]->sched_rq[entity->priority];
80c349dbc7Sjsg 
81c349dbc7Sjsg 	init_completion(&entity->entity_idle);
82c349dbc7Sjsg 
835ca02815Sjsg 	/* We start in an idle state. */
84f005ef32Sjsg 	complete_all(&entity->entity_idle);
855ca02815Sjsg 
8658a32cd0Sjsg 	mtx_init(&entity->rq_lock, IPL_NONE);
87c349dbc7Sjsg 	spsc_queue_init(&entity->job_queue);
88c349dbc7Sjsg 
89c349dbc7Sjsg 	atomic_set(&entity->fence_seq, 0);
90c349dbc7Sjsg 	entity->fence_context = dma_fence_context_alloc(2);
91c349dbc7Sjsg 
92c349dbc7Sjsg 	return 0;
93c349dbc7Sjsg }
94c349dbc7Sjsg EXPORT_SYMBOL(drm_sched_entity_init);
95c349dbc7Sjsg 
96c349dbc7Sjsg /**
97c349dbc7Sjsg  * drm_sched_entity_modify_sched - Modify sched of an entity
98c349dbc7Sjsg  * @entity: scheduler entity to init
99c349dbc7Sjsg  * @sched_list: the list of new drm scheds which will replace
100c349dbc7Sjsg  *		 existing entity->sched_list
101c349dbc7Sjsg  * @num_sched_list: number of drm sched in sched_list
1021bb76ff1Sjsg  *
1031bb76ff1Sjsg  * Note that this must be called under the same common lock for @entity as
1041bb76ff1Sjsg  * drm_sched_job_arm() and drm_sched_entity_push_job(), or the driver needs to
1051bb76ff1Sjsg  * guarantee through some other means that this is never called while new jobs
1061bb76ff1Sjsg  * can be pushed to @entity.
107c349dbc7Sjsg  */
drm_sched_entity_modify_sched(struct drm_sched_entity * entity,struct drm_gpu_scheduler ** sched_list,unsigned int num_sched_list)108c349dbc7Sjsg void drm_sched_entity_modify_sched(struct drm_sched_entity *entity,
109c349dbc7Sjsg 				    struct drm_gpu_scheduler **sched_list,
110c349dbc7Sjsg 				    unsigned int num_sched_list)
111c349dbc7Sjsg {
112c349dbc7Sjsg 	WARN_ON(!num_sched_list || !sched_list);
113c349dbc7Sjsg 
114*b9c19e87Sjsg 	spin_lock(&entity->rq_lock);
115c349dbc7Sjsg 	entity->sched_list = sched_list;
116c349dbc7Sjsg 	entity->num_sched_list = num_sched_list;
117*b9c19e87Sjsg 	spin_unlock(&entity->rq_lock);
118c349dbc7Sjsg }
119c349dbc7Sjsg EXPORT_SYMBOL(drm_sched_entity_modify_sched);
120c349dbc7Sjsg 
drm_sched_entity_is_idle(struct drm_sched_entity * entity)121c349dbc7Sjsg static bool drm_sched_entity_is_idle(struct drm_sched_entity *entity)
122c349dbc7Sjsg {
123c349dbc7Sjsg 	rmb(); /* for list_empty to work without lock */
124c349dbc7Sjsg 
125c349dbc7Sjsg 	if (list_empty(&entity->list) ||
1262ecbf4e0Sjsg 	    spsc_queue_count(&entity->job_queue) == 0 ||
1272ecbf4e0Sjsg 	    entity->stopped)
128c349dbc7Sjsg 		return true;
129c349dbc7Sjsg 
130c349dbc7Sjsg 	return false;
131c349dbc7Sjsg }
132c349dbc7Sjsg 
1331bb76ff1Sjsg /* Return true if entity could provide a job. */
drm_sched_entity_is_ready(struct drm_sched_entity * entity)134c349dbc7Sjsg bool drm_sched_entity_is_ready(struct drm_sched_entity *entity)
135c349dbc7Sjsg {
136c349dbc7Sjsg 	if (spsc_queue_peek(&entity->job_queue) == NULL)
137c349dbc7Sjsg 		return false;
138c349dbc7Sjsg 
139c349dbc7Sjsg 	if (READ_ONCE(entity->dependency))
140c349dbc7Sjsg 		return false;
141c349dbc7Sjsg 
142c349dbc7Sjsg 	return true;
143c349dbc7Sjsg }
144c349dbc7Sjsg 
145c349dbc7Sjsg /**
146f005ef32Sjsg  * drm_sched_entity_error - return error of last scheduled job
147f005ef32Sjsg  * @entity: scheduler entity to check
148f005ef32Sjsg  *
149f005ef32Sjsg  * Opportunistically return the error of the last scheduled job. Result can
150f005ef32Sjsg  * change any time when new jobs are pushed to the hw.
151f005ef32Sjsg  */
drm_sched_entity_error(struct drm_sched_entity * entity)152f005ef32Sjsg int drm_sched_entity_error(struct drm_sched_entity *entity)
153f005ef32Sjsg {
154f005ef32Sjsg 	struct dma_fence *fence;
155f005ef32Sjsg 	int r;
156f005ef32Sjsg 
157f005ef32Sjsg 	rcu_read_lock();
158f005ef32Sjsg 	fence = rcu_dereference(entity->last_scheduled);
159f005ef32Sjsg 	r = fence ? fence->error : 0;
160f005ef32Sjsg 	rcu_read_unlock();
161f005ef32Sjsg 
162f005ef32Sjsg 	return r;
163f005ef32Sjsg }
164f005ef32Sjsg EXPORT_SYMBOL(drm_sched_entity_error);
165f005ef32Sjsg 
drm_sched_entity_kill_jobs_work(struct work_struct * wrk)166f005ef32Sjsg static void drm_sched_entity_kill_jobs_work(struct work_struct *wrk)
167f005ef32Sjsg {
168f005ef32Sjsg 	struct drm_sched_job *job = container_of(wrk, typeof(*job), work);
169f005ef32Sjsg 
170f005ef32Sjsg 	drm_sched_fence_finished(job->s_fence, -ESRCH);
171f005ef32Sjsg 	WARN_ON(job->s_fence->parent);
172f005ef32Sjsg 	job->sched->ops->free_job(job);
173f005ef32Sjsg }
174f005ef32Sjsg 
175f005ef32Sjsg /* Signal the scheduler finished fence when the entity in question is killed. */
drm_sched_entity_kill_jobs_cb(struct dma_fence * f,struct dma_fence_cb * cb)176f005ef32Sjsg static void drm_sched_entity_kill_jobs_cb(struct dma_fence *f,
177f005ef32Sjsg 					  struct dma_fence_cb *cb)
178f005ef32Sjsg {
179f005ef32Sjsg 	struct drm_sched_job *job = container_of(cb, struct drm_sched_job,
180f005ef32Sjsg 						 finish_cb);
181f005ef32Sjsg 	unsigned long index;
182f005ef32Sjsg 
183f005ef32Sjsg 	dma_fence_put(f);
184f005ef32Sjsg 
185f005ef32Sjsg 	/* Wait for all dependencies to avoid data corruptions */
186f005ef32Sjsg 	xa_for_each(&job->dependencies, index, f) {
187f005ef32Sjsg 		struct drm_sched_fence *s_fence = to_drm_sched_fence(f);
188f005ef32Sjsg 
189f005ef32Sjsg 		if (s_fence && f == &s_fence->scheduled) {
190f005ef32Sjsg 			/* The dependencies array had a reference on the scheduled
191f005ef32Sjsg 			 * fence, and the finished fence refcount might have
192f005ef32Sjsg 			 * dropped to zero. Use dma_fence_get_rcu() so we get
193f005ef32Sjsg 			 * a NULL fence in that case.
194f005ef32Sjsg 			 */
195f005ef32Sjsg 			f = dma_fence_get_rcu(&s_fence->finished);
196f005ef32Sjsg 
197f005ef32Sjsg 			/* Now that we have a reference on the finished fence,
198f005ef32Sjsg 			 * we can release the reference the dependencies array
199f005ef32Sjsg 			 * had on the scheduled fence.
200f005ef32Sjsg 			 */
201f005ef32Sjsg 			dma_fence_put(&s_fence->scheduled);
202f005ef32Sjsg 		}
203f005ef32Sjsg 
204f005ef32Sjsg 		xa_erase(&job->dependencies, index);
205f005ef32Sjsg 		if (f && !dma_fence_add_callback(f, &job->finish_cb,
206f005ef32Sjsg 						 drm_sched_entity_kill_jobs_cb))
207f005ef32Sjsg 			return;
208f005ef32Sjsg 
209f005ef32Sjsg 		dma_fence_put(f);
210f005ef32Sjsg 	}
211f005ef32Sjsg 
212f005ef32Sjsg 	INIT_WORK(&job->work, drm_sched_entity_kill_jobs_work);
213f005ef32Sjsg 	schedule_work(&job->work);
214f005ef32Sjsg }
215f005ef32Sjsg 
216f005ef32Sjsg /* Remove the entity from the scheduler and kill all pending jobs */
drm_sched_entity_kill(struct drm_sched_entity * entity)217f005ef32Sjsg static void drm_sched_entity_kill(struct drm_sched_entity *entity)
218f005ef32Sjsg {
219f005ef32Sjsg 	struct drm_sched_job *job;
220f005ef32Sjsg 	struct dma_fence *prev;
221f005ef32Sjsg 
222f005ef32Sjsg 	if (!entity->rq)
223f005ef32Sjsg 		return;
224f005ef32Sjsg 
225f005ef32Sjsg 	spin_lock(&entity->rq_lock);
226f005ef32Sjsg 	entity->stopped = true;
227f005ef32Sjsg 	drm_sched_rq_remove_entity(entity->rq, entity);
228f005ef32Sjsg 	spin_unlock(&entity->rq_lock);
229f005ef32Sjsg 
230f005ef32Sjsg 	/* Make sure this entity is not used by the scheduler at the moment */
231f005ef32Sjsg 	wait_for_completion(&entity->entity_idle);
232f005ef32Sjsg 
233f005ef32Sjsg 	/* The entity is guaranteed to not be used by the scheduler */
234f005ef32Sjsg 	prev = rcu_dereference_check(entity->last_scheduled, true);
235f005ef32Sjsg 	dma_fence_get(prev);
236f005ef32Sjsg 	while ((job = to_drm_sched_job(spsc_queue_pop(&entity->job_queue)))) {
237f005ef32Sjsg 		struct drm_sched_fence *s_fence = job->s_fence;
238f005ef32Sjsg 
239f005ef32Sjsg 		dma_fence_get(&s_fence->finished);
240f005ef32Sjsg 		if (!prev || dma_fence_add_callback(prev, &job->finish_cb,
241f005ef32Sjsg 					   drm_sched_entity_kill_jobs_cb))
242f005ef32Sjsg 			drm_sched_entity_kill_jobs_cb(NULL, &job->finish_cb);
243f005ef32Sjsg 
244f005ef32Sjsg 		prev = &s_fence->finished;
245f005ef32Sjsg 	}
246f005ef32Sjsg 	dma_fence_put(prev);
247f005ef32Sjsg }
248f005ef32Sjsg 
249f005ef32Sjsg /**
250c349dbc7Sjsg  * drm_sched_entity_flush - Flush a context entity
251c349dbc7Sjsg  *
252c349dbc7Sjsg  * @entity: scheduler entity
253c349dbc7Sjsg  * @timeout: time to wait in for Q to become empty in jiffies.
254c349dbc7Sjsg  *
255c349dbc7Sjsg  * Splitting drm_sched_entity_fini() into two functions, The first one does the
256c349dbc7Sjsg  * waiting, removes the entity from the runqueue and returns an error when the
257c349dbc7Sjsg  * process was killed.
258c349dbc7Sjsg  *
259c349dbc7Sjsg  * Returns the remaining time in jiffies left from the input timeout
260c349dbc7Sjsg  */
drm_sched_entity_flush(struct drm_sched_entity * entity,long timeout)261c349dbc7Sjsg long drm_sched_entity_flush(struct drm_sched_entity *entity, long timeout)
262c349dbc7Sjsg {
263c349dbc7Sjsg 	struct drm_gpu_scheduler *sched;
264c349dbc7Sjsg #ifdef __linux__
265c349dbc7Sjsg 	struct task_struct *last_user;
266c349dbc7Sjsg #else
267c349dbc7Sjsg 	struct process *last_user, *curpr;
268c349dbc7Sjsg #endif
269c349dbc7Sjsg 	long ret = timeout;
270c349dbc7Sjsg 
271c349dbc7Sjsg 	if (!entity->rq)
272c349dbc7Sjsg 		return 0;
273c349dbc7Sjsg 
274c349dbc7Sjsg 	sched = entity->rq->sched;
275c349dbc7Sjsg 	/**
276c349dbc7Sjsg 	 * The client will not queue more IBs during this fini, consume existing
277c349dbc7Sjsg 	 * queued IBs or discard them on SIGKILL
278c349dbc7Sjsg 	 */
279c349dbc7Sjsg #ifdef __linux__
280c349dbc7Sjsg 	if (current->flags & PF_EXITING) {
281c349dbc7Sjsg #else
282c349dbc7Sjsg 	curpr = curproc->p_p;
283c349dbc7Sjsg 	if (curpr->ps_flags & PS_EXITING) {
284c349dbc7Sjsg #endif
285c349dbc7Sjsg 		if (timeout)
286c349dbc7Sjsg 			ret = wait_event_timeout(
287c349dbc7Sjsg 					sched->job_scheduled,
288c349dbc7Sjsg 					drm_sched_entity_is_idle(entity),
289c349dbc7Sjsg 					timeout);
290c349dbc7Sjsg 	} else {
291c349dbc7Sjsg 		wait_event_killable(sched->job_scheduled,
292c349dbc7Sjsg 				    drm_sched_entity_is_idle(entity));
293c349dbc7Sjsg 	}
294c349dbc7Sjsg 
295c349dbc7Sjsg 	/* For killed process disable any more IBs enqueue right now */
296c349dbc7Sjsg #ifdef __linux__
297c349dbc7Sjsg 	last_user = cmpxchg(&entity->last_user, current->group_leader, NULL);
298c349dbc7Sjsg 	if ((!last_user || last_user == current->group_leader) &&
299f005ef32Sjsg 	    (current->flags & PF_EXITING) && (current->exit_code == SIGKILL))
300c349dbc7Sjsg #else
301c349dbc7Sjsg 	last_user = cmpxchg(&entity->last_user, curpr, NULL);
302c349dbc7Sjsg 	if ((!last_user || last_user == curproc->p_p) &&
303c349dbc7Sjsg 	    (curpr->ps_flags & PS_EXITING) &&
304f005ef32Sjsg 	    (curpr->ps_xsig == SIGKILL))
305c349dbc7Sjsg #endif
306f005ef32Sjsg 		drm_sched_entity_kill(entity);
307c349dbc7Sjsg 
308c349dbc7Sjsg 	return ret;
309c349dbc7Sjsg }
310c349dbc7Sjsg EXPORT_SYMBOL(drm_sched_entity_flush);
311c349dbc7Sjsg 
312c349dbc7Sjsg /**
3135ca02815Sjsg  * drm_sched_entity_fini - Destroy a context entity
314c349dbc7Sjsg  *
315c349dbc7Sjsg  * @entity: scheduler entity
316c349dbc7Sjsg  *
3171bb76ff1Sjsg  * Cleanups up @entity which has been initialized by drm_sched_entity_init().
318c349dbc7Sjsg  *
3191bb76ff1Sjsg  * If there are potentially job still in flight or getting newly queued
3201bb76ff1Sjsg  * drm_sched_entity_flush() must be called first. This function then goes over
3211bb76ff1Sjsg  * the entity and signals all jobs with an error code if the process was killed.
322c349dbc7Sjsg  */
323c349dbc7Sjsg void drm_sched_entity_fini(struct drm_sched_entity *entity)
324c349dbc7Sjsg {
325c349dbc7Sjsg 	/*
326f005ef32Sjsg 	 * If consumption of existing IBs wasn't completed. Forcefully remove
327f005ef32Sjsg 	 * them here. Also makes sure that the scheduler won't touch this entity
328f005ef32Sjsg 	 * any more.
329c349dbc7Sjsg 	 */
330f005ef32Sjsg 	drm_sched_entity_kill(entity);
331c349dbc7Sjsg 
332c349dbc7Sjsg 	if (entity->dependency) {
333f005ef32Sjsg 		dma_fence_remove_callback(entity->dependency, &entity->cb);
334c349dbc7Sjsg 		dma_fence_put(entity->dependency);
335c349dbc7Sjsg 		entity->dependency = NULL;
336c349dbc7Sjsg 	}
337c349dbc7Sjsg 
338f005ef32Sjsg 	dma_fence_put(rcu_dereference_check(entity->last_scheduled, true));
339f005ef32Sjsg 	RCU_INIT_POINTER(entity->last_scheduled, NULL);
340c349dbc7Sjsg }
341c349dbc7Sjsg EXPORT_SYMBOL(drm_sched_entity_fini);
342c349dbc7Sjsg 
343c349dbc7Sjsg /**
3445ca02815Sjsg  * drm_sched_entity_destroy - Destroy a context entity
345c349dbc7Sjsg  * @entity: scheduler entity
346c349dbc7Sjsg  *
3471bb76ff1Sjsg  * Calls drm_sched_entity_flush() and drm_sched_entity_fini() as a
3481bb76ff1Sjsg  * convenience wrapper.
349c349dbc7Sjsg  */
350c349dbc7Sjsg void drm_sched_entity_destroy(struct drm_sched_entity *entity)
351c349dbc7Sjsg {
352c349dbc7Sjsg 	drm_sched_entity_flush(entity, MAX_WAIT_SCHED_ENTITY_Q_EMPTY);
353c349dbc7Sjsg 	drm_sched_entity_fini(entity);
354c349dbc7Sjsg }
355c349dbc7Sjsg EXPORT_SYMBOL(drm_sched_entity_destroy);
356c349dbc7Sjsg 
3571bb76ff1Sjsg /* drm_sched_entity_clear_dep - callback to clear the entities dependency */
358c349dbc7Sjsg static void drm_sched_entity_clear_dep(struct dma_fence *f,
359c349dbc7Sjsg 				       struct dma_fence_cb *cb)
360c349dbc7Sjsg {
361c349dbc7Sjsg 	struct drm_sched_entity *entity =
362c349dbc7Sjsg 		container_of(cb, struct drm_sched_entity, cb);
363c349dbc7Sjsg 
364c349dbc7Sjsg 	entity->dependency = NULL;
365c349dbc7Sjsg 	dma_fence_put(f);
366c349dbc7Sjsg }
367c349dbc7Sjsg 
3685ca02815Sjsg /*
369c349dbc7Sjsg  * drm_sched_entity_clear_dep - callback to clear the entities dependency and
370c349dbc7Sjsg  * wake up scheduler
371c349dbc7Sjsg  */
372c349dbc7Sjsg static void drm_sched_entity_wakeup(struct dma_fence *f,
373c349dbc7Sjsg 				    struct dma_fence_cb *cb)
374c349dbc7Sjsg {
375c349dbc7Sjsg 	struct drm_sched_entity *entity =
376c349dbc7Sjsg 		container_of(cb, struct drm_sched_entity, cb);
377c349dbc7Sjsg 
378c349dbc7Sjsg 	drm_sched_entity_clear_dep(f, cb);
379f005ef32Sjsg 	drm_sched_wakeup_if_can_queue(entity->rq->sched);
380c349dbc7Sjsg }
381c349dbc7Sjsg 
382c349dbc7Sjsg /**
383c349dbc7Sjsg  * drm_sched_entity_set_priority - Sets priority of the entity
384c349dbc7Sjsg  *
385c349dbc7Sjsg  * @entity: scheduler entity
386c349dbc7Sjsg  * @priority: scheduler priority
387c349dbc7Sjsg  *
388c349dbc7Sjsg  * Update the priority of runqueus used for the entity.
389c349dbc7Sjsg  */
390c349dbc7Sjsg void drm_sched_entity_set_priority(struct drm_sched_entity *entity,
391c349dbc7Sjsg 				   enum drm_sched_priority priority)
392c349dbc7Sjsg {
393c349dbc7Sjsg 	spin_lock(&entity->rq_lock);
394c349dbc7Sjsg 	entity->priority = priority;
395c349dbc7Sjsg 	spin_unlock(&entity->rq_lock);
396c349dbc7Sjsg }
397c349dbc7Sjsg EXPORT_SYMBOL(drm_sched_entity_set_priority);
398c349dbc7Sjsg 
3991bb76ff1Sjsg /*
400c349dbc7Sjsg  * Add a callback to the current dependency of the entity to wake up the
401c349dbc7Sjsg  * scheduler when the entity becomes available.
402c349dbc7Sjsg  */
403c349dbc7Sjsg static bool drm_sched_entity_add_dependency_cb(struct drm_sched_entity *entity)
404c349dbc7Sjsg {
405c349dbc7Sjsg 	struct drm_gpu_scheduler *sched = entity->rq->sched;
406c349dbc7Sjsg 	struct dma_fence *fence = entity->dependency;
407c349dbc7Sjsg 	struct drm_sched_fence *s_fence;
408c349dbc7Sjsg 
409c349dbc7Sjsg 	if (fence->context == entity->fence_context ||
410c349dbc7Sjsg 	    fence->context == entity->fence_context + 1) {
411c349dbc7Sjsg 		/*
412c349dbc7Sjsg 		 * Fence is a scheduled/finished fence from a job
413c349dbc7Sjsg 		 * which belongs to the same entity, we can ignore
414c349dbc7Sjsg 		 * fences from ourself
415c349dbc7Sjsg 		 */
416c349dbc7Sjsg 		dma_fence_put(entity->dependency);
417c349dbc7Sjsg 		return false;
418c349dbc7Sjsg 	}
419c349dbc7Sjsg 
420c349dbc7Sjsg 	s_fence = to_drm_sched_fence(fence);
421f005ef32Sjsg 	if (!fence->error && s_fence && s_fence->sched == sched &&
4221bb76ff1Sjsg 	    !test_bit(DRM_SCHED_FENCE_DONT_PIPELINE, &fence->flags)) {
423c349dbc7Sjsg 
424c349dbc7Sjsg 		/*
425c349dbc7Sjsg 		 * Fence is from the same scheduler, only need to wait for
426c349dbc7Sjsg 		 * it to be scheduled
427c349dbc7Sjsg 		 */
428c349dbc7Sjsg 		fence = dma_fence_get(&s_fence->scheduled);
429c349dbc7Sjsg 		dma_fence_put(entity->dependency);
430c349dbc7Sjsg 		entity->dependency = fence;
431c349dbc7Sjsg 		if (!dma_fence_add_callback(fence, &entity->cb,
432c349dbc7Sjsg 					    drm_sched_entity_clear_dep))
433c349dbc7Sjsg 			return true;
434c349dbc7Sjsg 
435c349dbc7Sjsg 		/* Ignore it when it is already scheduled */
436c349dbc7Sjsg 		dma_fence_put(fence);
437c349dbc7Sjsg 		return false;
438c349dbc7Sjsg 	}
439c349dbc7Sjsg 
440c349dbc7Sjsg 	if (!dma_fence_add_callback(entity->dependency, &entity->cb,
441c349dbc7Sjsg 				    drm_sched_entity_wakeup))
442c349dbc7Sjsg 		return true;
443c349dbc7Sjsg 
444c349dbc7Sjsg 	dma_fence_put(entity->dependency);
445c349dbc7Sjsg 	return false;
446c349dbc7Sjsg }
447c349dbc7Sjsg 
448f005ef32Sjsg static struct dma_fence *
449f005ef32Sjsg drm_sched_job_dependency(struct drm_sched_job *job,
450f005ef32Sjsg 			 struct drm_sched_entity *entity)
451f005ef32Sjsg {
452f005ef32Sjsg 	struct dma_fence *f;
453f005ef32Sjsg 
454f005ef32Sjsg 	/* We keep the fence around, so we can iterate over all dependencies
455f005ef32Sjsg 	 * in drm_sched_entity_kill_jobs_cb() to ensure all deps are signaled
456f005ef32Sjsg 	 * before killing the job.
457f005ef32Sjsg 	 */
458f005ef32Sjsg 	f = xa_load(&job->dependencies, job->last_dependency);
459f005ef32Sjsg 	if (f) {
460f005ef32Sjsg 		job->last_dependency++;
461f005ef32Sjsg 		return dma_fence_get(f);
462f005ef32Sjsg 	}
463f005ef32Sjsg 
464f005ef32Sjsg 	if (job->sched->ops->prepare_job)
465f005ef32Sjsg 		return job->sched->ops->prepare_job(job, entity);
466f005ef32Sjsg 
467f005ef32Sjsg 	return NULL;
468f005ef32Sjsg }
469f005ef32Sjsg 
470c349dbc7Sjsg struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity)
471c349dbc7Sjsg {
472c349dbc7Sjsg 	struct drm_sched_job *sched_job;
473c349dbc7Sjsg 
474c349dbc7Sjsg 	sched_job = to_drm_sched_job(spsc_queue_peek(&entity->job_queue));
475c349dbc7Sjsg 	if (!sched_job)
476c349dbc7Sjsg 		return NULL;
477c349dbc7Sjsg 
478c349dbc7Sjsg 	while ((entity->dependency =
4791bb76ff1Sjsg 			drm_sched_job_dependency(sched_job, entity))) {
480c349dbc7Sjsg 		trace_drm_sched_job_wait_dep(sched_job, entity->dependency);
481c349dbc7Sjsg 
482c349dbc7Sjsg 		if (drm_sched_entity_add_dependency_cb(entity))
483c349dbc7Sjsg 			return NULL;
484c349dbc7Sjsg 	}
485c349dbc7Sjsg 
486c349dbc7Sjsg 	/* skip jobs from entity that marked guilty */
487c349dbc7Sjsg 	if (entity->guilty && atomic_read(entity->guilty))
488c349dbc7Sjsg 		dma_fence_set_error(&sched_job->s_fence->finished, -ECANCELED);
489c349dbc7Sjsg 
490f005ef32Sjsg 	dma_fence_put(rcu_dereference_check(entity->last_scheduled, true));
491f005ef32Sjsg 	rcu_assign_pointer(entity->last_scheduled,
492f005ef32Sjsg 			   dma_fence_get(&sched_job->s_fence->finished));
493c349dbc7Sjsg 
4941bb76ff1Sjsg 	/*
4951bb76ff1Sjsg 	 * If the queue is empty we allow drm_sched_entity_select_rq() to
4961bb76ff1Sjsg 	 * locklessly access ->last_scheduled. This only works if we set the
4971bb76ff1Sjsg 	 * pointer before we dequeue and if we a write barrier here.
4981bb76ff1Sjsg 	 */
4991bb76ff1Sjsg 	smp_wmb();
5001bb76ff1Sjsg 
501c349dbc7Sjsg 	spsc_queue_pop(&entity->job_queue);
502f005ef32Sjsg 
503f005ef32Sjsg 	/*
504f005ef32Sjsg 	 * Update the entity's location in the min heap according to
505f005ef32Sjsg 	 * the timestamp of the next job, if any.
506f005ef32Sjsg 	 */
507f005ef32Sjsg 	if (drm_sched_policy == DRM_SCHED_POLICY_FIFO) {
508f005ef32Sjsg 		struct drm_sched_job *next;
509f005ef32Sjsg 
510f005ef32Sjsg 		next = to_drm_sched_job(spsc_queue_peek(&entity->job_queue));
511f005ef32Sjsg 		if (next)
512f005ef32Sjsg 			drm_sched_rq_update_fifo(entity, next->submit_ts);
513f005ef32Sjsg 	}
514f005ef32Sjsg 
515f005ef32Sjsg 	/* Jobs and entities might have different lifecycles. Since we're
516f005ef32Sjsg 	 * removing the job from the entities queue, set the jobs entity pointer
517f005ef32Sjsg 	 * to NULL to prevent any future access of the entity through this job.
518f005ef32Sjsg 	 */
519f005ef32Sjsg 	sched_job->entity = NULL;
520f005ef32Sjsg 
521c349dbc7Sjsg 	return sched_job;
522c349dbc7Sjsg }
523c349dbc7Sjsg 
524c349dbc7Sjsg void drm_sched_entity_select_rq(struct drm_sched_entity *entity)
525c349dbc7Sjsg {
526c349dbc7Sjsg 	struct dma_fence *fence;
527c349dbc7Sjsg 	struct drm_gpu_scheduler *sched;
528c349dbc7Sjsg 	struct drm_sched_rq *rq;
529c349dbc7Sjsg 
5301bb76ff1Sjsg 	/* single possible engine and already selected */
5311bb76ff1Sjsg 	if (!entity->sched_list)
532c349dbc7Sjsg 		return;
533c349dbc7Sjsg 
5341bb76ff1Sjsg 	/* queue non-empty, stay on the same engine */
5351bb76ff1Sjsg 	if (spsc_queue_count(&entity->job_queue))
5361bb76ff1Sjsg 		return;
5371bb76ff1Sjsg 
5381bb76ff1Sjsg 	/*
5391bb76ff1Sjsg 	 * Only when the queue is empty are we guaranteed that the scheduler
5401bb76ff1Sjsg 	 * thread cannot change ->last_scheduled. To enforce ordering we need
5411bb76ff1Sjsg 	 * a read barrier here. See drm_sched_entity_pop_job() for the other
5421bb76ff1Sjsg 	 * side.
5431bb76ff1Sjsg 	 */
5441bb76ff1Sjsg 	smp_rmb();
5451bb76ff1Sjsg 
546f005ef32Sjsg 	fence = rcu_dereference_check(entity->last_scheduled, true);
5471bb76ff1Sjsg 
5481bb76ff1Sjsg 	/* stay on the same engine if the previous job hasn't finished */
549c349dbc7Sjsg 	if (fence && !dma_fence_is_signaled(fence))
550c349dbc7Sjsg 		return;
551c349dbc7Sjsg 
552c349dbc7Sjsg 	spin_lock(&entity->rq_lock);
553c349dbc7Sjsg 	sched = drm_sched_pick_best(entity->sched_list, entity->num_sched_list);
554c349dbc7Sjsg 	rq = sched ? &sched->sched_rq[entity->priority] : NULL;
555c349dbc7Sjsg 	if (rq != entity->rq) {
556c349dbc7Sjsg 		drm_sched_rq_remove_entity(entity->rq, entity);
557c349dbc7Sjsg 		entity->rq = rq;
558c349dbc7Sjsg 	}
559c349dbc7Sjsg 	spin_unlock(&entity->rq_lock);
5605ca02815Sjsg 
5615ca02815Sjsg 	if (entity->num_sched_list == 1)
5625ca02815Sjsg 		entity->sched_list = NULL;
563c349dbc7Sjsg }
564c349dbc7Sjsg 
565c349dbc7Sjsg /**
566c349dbc7Sjsg  * drm_sched_entity_push_job - Submit a job to the entity's job queue
567c349dbc7Sjsg  * @sched_job: job to submit
568c349dbc7Sjsg  *
5691bb76ff1Sjsg  * Note: To guarantee that the order of insertion to queue matches the job's
5701bb76ff1Sjsg  * fence sequence number this function should be called with drm_sched_job_arm()
5711bb76ff1Sjsg  * under common lock for the struct drm_sched_entity that was set up for
5721bb76ff1Sjsg  * @sched_job in drm_sched_job_init().
573c349dbc7Sjsg  *
574c349dbc7Sjsg  * Returns 0 for success, negative error code otherwise.
575c349dbc7Sjsg  */
5761bb76ff1Sjsg void drm_sched_entity_push_job(struct drm_sched_job *sched_job)
577c349dbc7Sjsg {
5781bb76ff1Sjsg 	struct drm_sched_entity *entity = sched_job->entity;
579c349dbc7Sjsg 	bool first;
580f005ef32Sjsg 	ktime_t submit_ts;
581c349dbc7Sjsg 
582c349dbc7Sjsg 	trace_drm_sched_job(sched_job, entity);
5835ca02815Sjsg 	atomic_inc(entity->rq->sched->score);
584c349dbc7Sjsg #ifdef __linux__
585c349dbc7Sjsg 	WRITE_ONCE(entity->last_user, current->group_leader);
586c349dbc7Sjsg #else
587c349dbc7Sjsg 	WRITE_ONCE(entity->last_user, curproc->p_p);
588c349dbc7Sjsg #endif
589f005ef32Sjsg 
590f005ef32Sjsg 	/*
591f005ef32Sjsg 	 * After the sched_job is pushed into the entity queue, it may be
592f005ef32Sjsg 	 * completed and freed up at any time. We can no longer access it.
593f005ef32Sjsg 	 * Make sure to set the submit_ts first, to avoid a race.
594f005ef32Sjsg 	 */
595f005ef32Sjsg 	sched_job->submit_ts = submit_ts = ktime_get();
596c349dbc7Sjsg 	first = spsc_queue_push(&entity->job_queue, &sched_job->queue_node);
597c349dbc7Sjsg 
598c349dbc7Sjsg 	/* first job wakes up scheduler */
599c349dbc7Sjsg 	if (first) {
600c349dbc7Sjsg 		/* Add the entity to the run queue */
601c349dbc7Sjsg 		spin_lock(&entity->rq_lock);
602c349dbc7Sjsg 		if (entity->stopped) {
603c349dbc7Sjsg 			spin_unlock(&entity->rq_lock);
604c349dbc7Sjsg 
605c349dbc7Sjsg 			DRM_ERROR("Trying to push to a killed entity\n");
606c349dbc7Sjsg 			return;
607c349dbc7Sjsg 		}
608f005ef32Sjsg 
609c349dbc7Sjsg 		drm_sched_rq_add_entity(entity->rq, entity);
610c349dbc7Sjsg 		spin_unlock(&entity->rq_lock);
611f005ef32Sjsg 
612f005ef32Sjsg 		if (drm_sched_policy == DRM_SCHED_POLICY_FIFO)
613f005ef32Sjsg 			drm_sched_rq_update_fifo(entity, submit_ts);
614f005ef32Sjsg 
615f005ef32Sjsg 		drm_sched_wakeup_if_can_queue(entity->rq->sched);
616c349dbc7Sjsg 	}
617c349dbc7Sjsg }
618c349dbc7Sjsg EXPORT_SYMBOL(drm_sched_entity_push_job);
619