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 */ 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 */ 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 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. */ 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 */ 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 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. */ 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 */ 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 */ 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