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