18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * SPDX-License-Identifier: MIT 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright © 2018 Intel Corporation 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/mutex.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "i915_drv.h" 108c2ecf20Sopenharmony_ci#include "i915_globals.h" 118c2ecf20Sopenharmony_ci#include "i915_request.h" 128c2ecf20Sopenharmony_ci#include "i915_scheduler.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic struct i915_global_scheduler { 158c2ecf20Sopenharmony_ci struct i915_global base; 168c2ecf20Sopenharmony_ci struct kmem_cache *slab_dependencies; 178c2ecf20Sopenharmony_ci struct kmem_cache *slab_priorities; 188c2ecf20Sopenharmony_ci} global; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(schedule_lock); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic const struct i915_request * 238c2ecf20Sopenharmony_cinode_to_request(const struct i915_sched_node *node) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci return container_of(node, const struct i915_request, sched); 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic inline bool node_started(const struct i915_sched_node *node) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci return i915_request_started(node_to_request(node)); 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic inline bool node_signaled(const struct i915_sched_node *node) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci return i915_request_completed(node_to_request(node)); 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic inline struct i915_priolist *to_priolist(struct rb_node *rb) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci return rb_entry(rb, struct i915_priolist, node); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic void assert_priolists(struct intel_engine_execlists * const execlists) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct rb_node *rb; 468c2ecf20Sopenharmony_ci long last_prio, i; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)) 498c2ecf20Sopenharmony_ci return; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci GEM_BUG_ON(rb_first_cached(&execlists->queue) != 528c2ecf20Sopenharmony_ci rb_first(&execlists->queue.rb_root)); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci last_prio = INT_MAX; 558c2ecf20Sopenharmony_ci for (rb = rb_first_cached(&execlists->queue); rb; rb = rb_next(rb)) { 568c2ecf20Sopenharmony_ci const struct i915_priolist *p = to_priolist(rb); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci GEM_BUG_ON(p->priority > last_prio); 598c2ecf20Sopenharmony_ci last_prio = p->priority; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci GEM_BUG_ON(!p->used); 628c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(p->requests); i++) { 638c2ecf20Sopenharmony_ci if (list_empty(&p->requests[i])) 648c2ecf20Sopenharmony_ci continue; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci GEM_BUG_ON(!(p->used & BIT(i))); 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistruct list_head * 728c2ecf20Sopenharmony_cii915_sched_lookup_priolist(struct intel_engine_cs *engine, int prio) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct intel_engine_execlists * const execlists = &engine->execlists; 758c2ecf20Sopenharmony_ci struct i915_priolist *p; 768c2ecf20Sopenharmony_ci struct rb_node **parent, *rb; 778c2ecf20Sopenharmony_ci bool first = true; 788c2ecf20Sopenharmony_ci int idx, i; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci lockdep_assert_held(&engine->active.lock); 818c2ecf20Sopenharmony_ci assert_priolists(execlists); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* buckets sorted from highest [in slot 0] to lowest priority */ 848c2ecf20Sopenharmony_ci idx = I915_PRIORITY_COUNT - (prio & I915_PRIORITY_MASK) - 1; 858c2ecf20Sopenharmony_ci prio >>= I915_USER_PRIORITY_SHIFT; 868c2ecf20Sopenharmony_ci if (unlikely(execlists->no_priolist)) 878c2ecf20Sopenharmony_ci prio = I915_PRIORITY_NORMAL; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cifind_priolist: 908c2ecf20Sopenharmony_ci /* most positive priority is scheduled first, equal priorities fifo */ 918c2ecf20Sopenharmony_ci rb = NULL; 928c2ecf20Sopenharmony_ci parent = &execlists->queue.rb_root.rb_node; 938c2ecf20Sopenharmony_ci while (*parent) { 948c2ecf20Sopenharmony_ci rb = *parent; 958c2ecf20Sopenharmony_ci p = to_priolist(rb); 968c2ecf20Sopenharmony_ci if (prio > p->priority) { 978c2ecf20Sopenharmony_ci parent = &rb->rb_left; 988c2ecf20Sopenharmony_ci } else if (prio < p->priority) { 998c2ecf20Sopenharmony_ci parent = &rb->rb_right; 1008c2ecf20Sopenharmony_ci first = false; 1018c2ecf20Sopenharmony_ci } else { 1028c2ecf20Sopenharmony_ci goto out; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (prio == I915_PRIORITY_NORMAL) { 1078c2ecf20Sopenharmony_ci p = &execlists->default_priolist; 1088c2ecf20Sopenharmony_ci } else { 1098c2ecf20Sopenharmony_ci p = kmem_cache_alloc(global.slab_priorities, GFP_ATOMIC); 1108c2ecf20Sopenharmony_ci /* Convert an allocation failure to a priority bump */ 1118c2ecf20Sopenharmony_ci if (unlikely(!p)) { 1128c2ecf20Sopenharmony_ci prio = I915_PRIORITY_NORMAL; /* recurses just once */ 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* To maintain ordering with all rendering, after an 1158c2ecf20Sopenharmony_ci * allocation failure we have to disable all scheduling. 1168c2ecf20Sopenharmony_ci * Requests will then be executed in fifo, and schedule 1178c2ecf20Sopenharmony_ci * will ensure that dependencies are emitted in fifo. 1188c2ecf20Sopenharmony_ci * There will be still some reordering with existing 1198c2ecf20Sopenharmony_ci * requests, so if userspace lied about their 1208c2ecf20Sopenharmony_ci * dependencies that reordering may be visible. 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ci execlists->no_priolist = true; 1238c2ecf20Sopenharmony_ci goto find_priolist; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci p->priority = prio; 1288c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(p->requests); i++) 1298c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&p->requests[i]); 1308c2ecf20Sopenharmony_ci rb_link_node(&p->node, rb, parent); 1318c2ecf20Sopenharmony_ci rb_insert_color_cached(&p->node, &execlists->queue, first); 1328c2ecf20Sopenharmony_ci p->used = 0; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ciout: 1358c2ecf20Sopenharmony_ci p->used |= BIT(idx); 1368c2ecf20Sopenharmony_ci return &p->requests[idx]; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_civoid __i915_priolist_free(struct i915_priolist *p) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci kmem_cache_free(global.slab_priorities, p); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistruct sched_cache { 1458c2ecf20Sopenharmony_ci struct list_head *priolist; 1468c2ecf20Sopenharmony_ci}; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic struct intel_engine_cs * 1498c2ecf20Sopenharmony_cisched_lock_engine(const struct i915_sched_node *node, 1508c2ecf20Sopenharmony_ci struct intel_engine_cs *locked, 1518c2ecf20Sopenharmony_ci struct sched_cache *cache) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci const struct i915_request *rq = node_to_request(node); 1548c2ecf20Sopenharmony_ci struct intel_engine_cs *engine; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci GEM_BUG_ON(!locked); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* 1598c2ecf20Sopenharmony_ci * Virtual engines complicate acquiring the engine timeline lock, 1608c2ecf20Sopenharmony_ci * as their rq->engine pointer is not stable until under that 1618c2ecf20Sopenharmony_ci * engine lock. The simple ploy we use is to take the lock then 1628c2ecf20Sopenharmony_ci * check that the rq still belongs to the newly locked engine. 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_ci while (locked != (engine = READ_ONCE(rq->engine))) { 1658c2ecf20Sopenharmony_ci spin_unlock(&locked->active.lock); 1668c2ecf20Sopenharmony_ci memset(cache, 0, sizeof(*cache)); 1678c2ecf20Sopenharmony_ci spin_lock(&engine->active.lock); 1688c2ecf20Sopenharmony_ci locked = engine; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci GEM_BUG_ON(locked != engine); 1728c2ecf20Sopenharmony_ci return locked; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic inline int rq_prio(const struct i915_request *rq) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci return rq->sched.attr.priority; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic inline bool need_preempt(int prio, int active) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci /* 1838c2ecf20Sopenharmony_ci * Allow preemption of low -> normal -> high, but we do 1848c2ecf20Sopenharmony_ci * not allow low priority tasks to preempt other low priority 1858c2ecf20Sopenharmony_ci * tasks under the impression that latency for low priority 1868c2ecf20Sopenharmony_ci * tasks does not matter (as much as background throughput), 1878c2ecf20Sopenharmony_ci * so kiss. 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_ci return prio >= max(I915_PRIORITY_NORMAL, active); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic void kick_submission(struct intel_engine_cs *engine, 1938c2ecf20Sopenharmony_ci const struct i915_request *rq, 1948c2ecf20Sopenharmony_ci int prio) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci const struct i915_request *inflight; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* 1998c2ecf20Sopenharmony_ci * We only need to kick the tasklet once for the high priority 2008c2ecf20Sopenharmony_ci * new context we add into the queue. 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci if (prio <= engine->execlists.queue_priority_hint) 2038c2ecf20Sopenharmony_ci return; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci rcu_read_lock(); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* Nothing currently active? We're overdue for a submission! */ 2088c2ecf20Sopenharmony_ci inflight = execlists_active(&engine->execlists); 2098c2ecf20Sopenharmony_ci if (!inflight) 2108c2ecf20Sopenharmony_ci goto unlock; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* 2138c2ecf20Sopenharmony_ci * If we are already the currently executing context, don't 2148c2ecf20Sopenharmony_ci * bother evaluating if we should preempt ourselves. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci if (inflight->context == rq->context) 2178c2ecf20Sopenharmony_ci goto unlock; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci ENGINE_TRACE(engine, 2208c2ecf20Sopenharmony_ci "bumping queue-priority-hint:%d for rq:%llx:%lld, inflight:%llx:%lld prio %d\n", 2218c2ecf20Sopenharmony_ci prio, 2228c2ecf20Sopenharmony_ci rq->fence.context, rq->fence.seqno, 2238c2ecf20Sopenharmony_ci inflight->fence.context, inflight->fence.seqno, 2248c2ecf20Sopenharmony_ci inflight->sched.attr.priority); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci engine->execlists.queue_priority_hint = prio; 2278c2ecf20Sopenharmony_ci if (need_preempt(prio, rq_prio(inflight))) 2288c2ecf20Sopenharmony_ci tasklet_hi_schedule(&engine->execlists.tasklet); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ciunlock: 2318c2ecf20Sopenharmony_ci rcu_read_unlock(); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic void __i915_schedule(struct i915_sched_node *node, 2358c2ecf20Sopenharmony_ci const struct i915_sched_attr *attr) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci const int prio = max(attr->priority, node->attr.priority); 2388c2ecf20Sopenharmony_ci struct intel_engine_cs *engine; 2398c2ecf20Sopenharmony_ci struct i915_dependency *dep, *p; 2408c2ecf20Sopenharmony_ci struct i915_dependency stack; 2418c2ecf20Sopenharmony_ci struct sched_cache cache; 2428c2ecf20Sopenharmony_ci LIST_HEAD(dfs); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* Needed in order to use the temporary link inside i915_dependency */ 2458c2ecf20Sopenharmony_ci lockdep_assert_held(&schedule_lock); 2468c2ecf20Sopenharmony_ci GEM_BUG_ON(prio == I915_PRIORITY_INVALID); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (node_signaled(node)) 2498c2ecf20Sopenharmony_ci return; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci stack.signaler = node; 2528c2ecf20Sopenharmony_ci list_add(&stack.dfs_link, &dfs); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* 2558c2ecf20Sopenharmony_ci * Recursively bump all dependent priorities to match the new request. 2568c2ecf20Sopenharmony_ci * 2578c2ecf20Sopenharmony_ci * A naive approach would be to use recursion: 2588c2ecf20Sopenharmony_ci * static void update_priorities(struct i915_sched_node *node, prio) { 2598c2ecf20Sopenharmony_ci * list_for_each_entry(dep, &node->signalers_list, signal_link) 2608c2ecf20Sopenharmony_ci * update_priorities(dep->signal, prio) 2618c2ecf20Sopenharmony_ci * queue_request(node); 2628c2ecf20Sopenharmony_ci * } 2638c2ecf20Sopenharmony_ci * but that may have unlimited recursion depth and so runs a very 2648c2ecf20Sopenharmony_ci * real risk of overunning the kernel stack. Instead, we build 2658c2ecf20Sopenharmony_ci * a flat list of all dependencies starting with the current request. 2668c2ecf20Sopenharmony_ci * As we walk the list of dependencies, we add all of its dependencies 2678c2ecf20Sopenharmony_ci * to the end of the list (this may include an already visited 2688c2ecf20Sopenharmony_ci * request) and continue to walk onwards onto the new dependencies. The 2698c2ecf20Sopenharmony_ci * end result is a topological list of requests in reverse order, the 2708c2ecf20Sopenharmony_ci * last element in the list is the request we must execute first. 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_ci list_for_each_entry(dep, &dfs, dfs_link) { 2738c2ecf20Sopenharmony_ci struct i915_sched_node *node = dep->signaler; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* If we are already flying, we know we have no signalers */ 2768c2ecf20Sopenharmony_ci if (node_started(node)) 2778c2ecf20Sopenharmony_ci continue; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* 2808c2ecf20Sopenharmony_ci * Within an engine, there can be no cycle, but we may 2818c2ecf20Sopenharmony_ci * refer to the same dependency chain multiple times 2828c2ecf20Sopenharmony_ci * (redundant dependencies are not eliminated) and across 2838c2ecf20Sopenharmony_ci * engines. 2848c2ecf20Sopenharmony_ci */ 2858c2ecf20Sopenharmony_ci list_for_each_entry(p, &node->signalers_list, signal_link) { 2868c2ecf20Sopenharmony_ci GEM_BUG_ON(p == dep); /* no cycles! */ 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (node_signaled(p->signaler)) 2898c2ecf20Sopenharmony_ci continue; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (prio > READ_ONCE(p->signaler->attr.priority)) 2928c2ecf20Sopenharmony_ci list_move_tail(&p->dfs_link, &dfs); 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* 2978c2ecf20Sopenharmony_ci * If we didn't need to bump any existing priorities, and we haven't 2988c2ecf20Sopenharmony_ci * yet submitted this request (i.e. there is no potential race with 2998c2ecf20Sopenharmony_ci * execlists_submit_request()), we can set our own priority and skip 3008c2ecf20Sopenharmony_ci * acquiring the engine locks. 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_ci if (node->attr.priority == I915_PRIORITY_INVALID) { 3038c2ecf20Sopenharmony_ci GEM_BUG_ON(!list_empty(&node->link)); 3048c2ecf20Sopenharmony_ci node->attr = *attr; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (stack.dfs_link.next == stack.dfs_link.prev) 3078c2ecf20Sopenharmony_ci return; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci __list_del_entry(&stack.dfs_link); 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci memset(&cache, 0, sizeof(cache)); 3138c2ecf20Sopenharmony_ci engine = node_to_request(node)->engine; 3148c2ecf20Sopenharmony_ci spin_lock(&engine->active.lock); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* Fifo and depth-first replacement ensure our deps execute before us */ 3178c2ecf20Sopenharmony_ci engine = sched_lock_engine(node, engine, &cache); 3188c2ecf20Sopenharmony_ci list_for_each_entry_safe_reverse(dep, p, &dfs, dfs_link) { 3198c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dep->dfs_link); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci node = dep->signaler; 3228c2ecf20Sopenharmony_ci engine = sched_lock_engine(node, engine, &cache); 3238c2ecf20Sopenharmony_ci lockdep_assert_held(&engine->active.lock); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* Recheck after acquiring the engine->timeline.lock */ 3268c2ecf20Sopenharmony_ci if (prio <= node->attr.priority || node_signaled(node)) 3278c2ecf20Sopenharmony_ci continue; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci GEM_BUG_ON(node_to_request(node)->engine != engine); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci WRITE_ONCE(node->attr.priority, prio); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci /* 3348c2ecf20Sopenharmony_ci * Once the request is ready, it will be placed into the 3358c2ecf20Sopenharmony_ci * priority lists and then onto the HW runlist. Before the 3368c2ecf20Sopenharmony_ci * request is ready, it does not contribute to our preemption 3378c2ecf20Sopenharmony_ci * decisions and we can safely ignore it, as it will, and 3388c2ecf20Sopenharmony_ci * any preemption required, be dealt with upon submission. 3398c2ecf20Sopenharmony_ci * See engine->submit_request() 3408c2ecf20Sopenharmony_ci */ 3418c2ecf20Sopenharmony_ci if (list_empty(&node->link)) 3428c2ecf20Sopenharmony_ci continue; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (i915_request_in_priority_queue(node_to_request(node))) { 3458c2ecf20Sopenharmony_ci if (!cache.priolist) 3468c2ecf20Sopenharmony_ci cache.priolist = 3478c2ecf20Sopenharmony_ci i915_sched_lookup_priolist(engine, 3488c2ecf20Sopenharmony_ci prio); 3498c2ecf20Sopenharmony_ci list_move_tail(&node->link, cache.priolist); 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* Defer (tasklet) submission until after all of our updates. */ 3538c2ecf20Sopenharmony_ci kick_submission(engine, node_to_request(node), prio); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci spin_unlock(&engine->active.lock); 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_civoid i915_schedule(struct i915_request *rq, const struct i915_sched_attr *attr) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci spin_lock_irq(&schedule_lock); 3628c2ecf20Sopenharmony_ci __i915_schedule(&rq->sched, attr); 3638c2ecf20Sopenharmony_ci spin_unlock_irq(&schedule_lock); 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic void __bump_priority(struct i915_sched_node *node, unsigned int bump) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct i915_sched_attr attr = node->attr; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (attr.priority & bump) 3718c2ecf20Sopenharmony_ci return; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci attr.priority |= bump; 3748c2ecf20Sopenharmony_ci __i915_schedule(node, &attr); 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_civoid i915_schedule_bump_priority(struct i915_request *rq, unsigned int bump) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci unsigned long flags; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci GEM_BUG_ON(bump & ~I915_PRIORITY_MASK); 3828c2ecf20Sopenharmony_ci if (READ_ONCE(rq->sched.attr.priority) & bump) 3838c2ecf20Sopenharmony_ci return; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci spin_lock_irqsave(&schedule_lock, flags); 3868c2ecf20Sopenharmony_ci __bump_priority(&rq->sched, bump); 3878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&schedule_lock, flags); 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_civoid i915_sched_node_init(struct i915_sched_node *node) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&node->signalers_list); 3938c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&node->waiters_list); 3948c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&node->link); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci i915_sched_node_reinit(node); 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_civoid i915_sched_node_reinit(struct i915_sched_node *node) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci node->attr.priority = I915_PRIORITY_INVALID; 4028c2ecf20Sopenharmony_ci node->semaphores = 0; 4038c2ecf20Sopenharmony_ci node->flags = 0; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci GEM_BUG_ON(!list_empty(&node->signalers_list)); 4068c2ecf20Sopenharmony_ci GEM_BUG_ON(!list_empty(&node->waiters_list)); 4078c2ecf20Sopenharmony_ci GEM_BUG_ON(!list_empty(&node->link)); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic struct i915_dependency * 4118c2ecf20Sopenharmony_cii915_dependency_alloc(void) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci return kmem_cache_alloc(global.slab_dependencies, GFP_KERNEL); 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic void 4178c2ecf20Sopenharmony_cii915_dependency_free(struct i915_dependency *dep) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci kmem_cache_free(global.slab_dependencies, dep); 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cibool __i915_sched_node_add_dependency(struct i915_sched_node *node, 4238c2ecf20Sopenharmony_ci struct i915_sched_node *signal, 4248c2ecf20Sopenharmony_ci struct i915_dependency *dep, 4258c2ecf20Sopenharmony_ci unsigned long flags) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci bool ret = false; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci spin_lock_irq(&schedule_lock); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (!node_signaled(signal)) { 4328c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dep->dfs_link); 4338c2ecf20Sopenharmony_ci dep->signaler = signal; 4348c2ecf20Sopenharmony_ci dep->waiter = node; 4358c2ecf20Sopenharmony_ci dep->flags = flags; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* All set, now publish. Beware the lockless walkers. */ 4388c2ecf20Sopenharmony_ci list_add_rcu(&dep->signal_link, &node->signalers_list); 4398c2ecf20Sopenharmony_ci list_add_rcu(&dep->wait_link, &signal->waiters_list); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* Propagate the chains */ 4428c2ecf20Sopenharmony_ci node->flags |= signal->flags; 4438c2ecf20Sopenharmony_ci ret = true; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci spin_unlock_irq(&schedule_lock); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci return ret; 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ciint i915_sched_node_add_dependency(struct i915_sched_node *node, 4528c2ecf20Sopenharmony_ci struct i915_sched_node *signal, 4538c2ecf20Sopenharmony_ci unsigned long flags) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci struct i915_dependency *dep; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci dep = i915_dependency_alloc(); 4588c2ecf20Sopenharmony_ci if (!dep) 4598c2ecf20Sopenharmony_ci return -ENOMEM; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci local_bh_disable(); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (!__i915_sched_node_add_dependency(node, signal, dep, 4648c2ecf20Sopenharmony_ci flags | I915_DEPENDENCY_ALLOC)) 4658c2ecf20Sopenharmony_ci i915_dependency_free(dep); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci local_bh_enable(); /* kick submission tasklet */ 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci return 0; 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_civoid i915_sched_node_fini(struct i915_sched_node *node) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct i915_dependency *dep, *tmp; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci spin_lock_irq(&schedule_lock); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* 4798c2ecf20Sopenharmony_ci * Everyone we depended upon (the fences we wait to be signaled) 4808c2ecf20Sopenharmony_ci * should retire before us and remove themselves from our list. 4818c2ecf20Sopenharmony_ci * However, retirement is run independently on each timeline and 4828c2ecf20Sopenharmony_ci * so we may be called out-of-order. 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_ci list_for_each_entry_safe(dep, tmp, &node->signalers_list, signal_link) { 4858c2ecf20Sopenharmony_ci GEM_BUG_ON(!list_empty(&dep->dfs_link)); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci list_del_rcu(&dep->wait_link); 4888c2ecf20Sopenharmony_ci if (dep->flags & I915_DEPENDENCY_ALLOC) 4898c2ecf20Sopenharmony_ci i915_dependency_free(dep); 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&node->signalers_list); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* Remove ourselves from everyone who depends upon us */ 4948c2ecf20Sopenharmony_ci list_for_each_entry_safe(dep, tmp, &node->waiters_list, wait_link) { 4958c2ecf20Sopenharmony_ci GEM_BUG_ON(dep->signaler != node); 4968c2ecf20Sopenharmony_ci GEM_BUG_ON(!list_empty(&dep->dfs_link)); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci list_del_rcu(&dep->signal_link); 4998c2ecf20Sopenharmony_ci if (dep->flags & I915_DEPENDENCY_ALLOC) 5008c2ecf20Sopenharmony_ci i915_dependency_free(dep); 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&node->waiters_list); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci spin_unlock_irq(&schedule_lock); 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic void i915_global_scheduler_shrink(void) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci kmem_cache_shrink(global.slab_dependencies); 5108c2ecf20Sopenharmony_ci kmem_cache_shrink(global.slab_priorities); 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic void i915_global_scheduler_exit(void) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci kmem_cache_destroy(global.slab_dependencies); 5168c2ecf20Sopenharmony_ci kmem_cache_destroy(global.slab_priorities); 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic struct i915_global_scheduler global = { { 5208c2ecf20Sopenharmony_ci .shrink = i915_global_scheduler_shrink, 5218c2ecf20Sopenharmony_ci .exit = i915_global_scheduler_exit, 5228c2ecf20Sopenharmony_ci} }; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ciint __init i915_global_scheduler_init(void) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci global.slab_dependencies = KMEM_CACHE(i915_dependency, 5278c2ecf20Sopenharmony_ci SLAB_HWCACHE_ALIGN | 5288c2ecf20Sopenharmony_ci SLAB_TYPESAFE_BY_RCU); 5298c2ecf20Sopenharmony_ci if (!global.slab_dependencies) 5308c2ecf20Sopenharmony_ci return -ENOMEM; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci global.slab_priorities = KMEM_CACHE(i915_priolist, 5338c2ecf20Sopenharmony_ci SLAB_HWCACHE_ALIGN); 5348c2ecf20Sopenharmony_ci if (!global.slab_priorities) 5358c2ecf20Sopenharmony_ci goto err_priorities; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci i915_global_register(&global.base); 5388c2ecf20Sopenharmony_ci return 0; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cierr_priorities: 5418c2ecf20Sopenharmony_ci kmem_cache_destroy(global.slab_priorities); 5428c2ecf20Sopenharmony_ci return -ENOMEM; 5438c2ecf20Sopenharmony_ci} 544