drm/i915: Allocate active tracking nodes from a slabcache
[linux-2.6-block.git] / drivers / gpu / drm / i915 / i915_active.c
CommitLineData
64d6c500
CW
1/*
2 * SPDX-License-Identifier: MIT
3 *
4 * Copyright © 2019 Intel Corporation
5 */
6
7#include "i915_drv.h"
8#include "i915_active.h"
9
10#define BKL(ref) (&(ref)->i915->drm.struct_mutex)
11
5f5c139d
CW
12/*
13 * Active refs memory management
14 *
15 * To be more economical with memory, we reap all the i915_active trees as
16 * they idle (when we know the active requests are inactive) and allocate the
17 * nodes from a local slab cache to hopefully reduce the fragmentation.
18 */
19static struct i915_global_active {
20 struct kmem_cache *slab_cache;
21} global;
22
64d6c500
CW
23struct active_node {
24 struct i915_gem_active base;
25 struct i915_active *ref;
26 struct rb_node node;
27 u64 timeline;
28};
29
a42375af
CW
30static void
31__active_park(struct i915_active *ref)
32{
33 struct active_node *it, *n;
34
35 rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
36 GEM_BUG_ON(i915_gem_active_isset(&it->base));
5f5c139d 37 kmem_cache_free(global.slab_cache, it);
a42375af
CW
38 }
39 ref->tree = RB_ROOT;
40}
41
64d6c500
CW
42static void
43__active_retire(struct i915_active *ref)
44{
45 GEM_BUG_ON(!ref->count);
a42375af
CW
46 if (--ref->count)
47 return;
48
49 /* return the unused nodes to our slabcache */
50 __active_park(ref);
51
52 ref->retire(ref);
64d6c500
CW
53}
54
55static void
56node_retire(struct i915_gem_active *base, struct i915_request *rq)
57{
58 __active_retire(container_of(base, struct active_node, base)->ref);
59}
60
61static void
62last_retire(struct i915_gem_active *base, struct i915_request *rq)
63{
64 __active_retire(container_of(base, struct i915_active, last));
65}
66
67static struct i915_gem_active *
68active_instance(struct i915_active *ref, u64 idx)
69{
70 struct active_node *node;
71 struct rb_node **p, *parent;
72 struct i915_request *old;
73
74 /*
75 * We track the most recently used timeline to skip a rbtree search
76 * for the common case, under typical loads we never need the rbtree
77 * at all. We can reuse the last slot if it is empty, that is
78 * after the previous activity has been retired, or if it matches the
79 * current timeline.
80 *
81 * Note that we allow the timeline to be active simultaneously in
82 * the rbtree and the last cache. We do this to avoid having
83 * to search and replace the rbtree element for a new timeline, with
84 * the cost being that we must be aware that the ref may be retired
85 * twice for the same timeline (as the older rbtree element will be
86 * retired before the new request added to last).
87 */
88 old = i915_gem_active_raw(&ref->last, BKL(ref));
89 if (!old || old->fence.context == idx)
90 goto out;
91
92 /* Move the currently active fence into the rbtree */
93 idx = old->fence.context;
94
95 parent = NULL;
96 p = &ref->tree.rb_node;
97 while (*p) {
98 parent = *p;
99
100 node = rb_entry(parent, struct active_node, node);
101 if (node->timeline == idx)
102 goto replace;
103
104 if (node->timeline < idx)
105 p = &parent->rb_right;
106 else
107 p = &parent->rb_left;
108 }
109
5f5c139d 110 node = kmem_cache_alloc(global.slab_cache, GFP_KERNEL);
64d6c500
CW
111
112 /* kmalloc may retire the ref->last (thanks shrinker)! */
113 if (unlikely(!i915_gem_active_raw(&ref->last, BKL(ref)))) {
5f5c139d 114 kmem_cache_free(global.slab_cache, node);
64d6c500
CW
115 goto out;
116 }
117
118 if (unlikely(!node))
119 return ERR_PTR(-ENOMEM);
120
121 init_request_active(&node->base, node_retire);
122 node->ref = ref;
123 node->timeline = idx;
124
125 rb_link_node(&node->node, parent, p);
126 rb_insert_color(&node->node, &ref->tree);
127
128replace:
129 /*
130 * Overwrite the previous active slot in the rbtree with last,
131 * leaving last zeroed. If the previous slot is still active,
132 * we must be careful as we now only expect to receive one retire
133 * callback not two, and so much undo the active counting for the
134 * overwritten slot.
135 */
136 if (i915_gem_active_isset(&node->base)) {
137 /* Retire ourselves from the old rq->active_list */
138 __list_del_entry(&node->base.link);
139 ref->count--;
140 GEM_BUG_ON(!ref->count);
141 }
142 GEM_BUG_ON(list_empty(&ref->last.link));
143 list_replace_init(&ref->last.link, &node->base.link);
144 node->base.request = fetch_and_zero(&ref->last.request);
145
146out:
147 return &ref->last;
148}
149
150void i915_active_init(struct drm_i915_private *i915,
151 struct i915_active *ref,
152 void (*retire)(struct i915_active *ref))
153{
154 ref->i915 = i915;
155 ref->retire = retire;
156 ref->tree = RB_ROOT;
157 init_request_active(&ref->last, last_retire);
158 ref->count = 0;
159}
160
161int i915_active_ref(struct i915_active *ref,
162 u64 timeline,
163 struct i915_request *rq)
164{
165 struct i915_gem_active *active;
166
167 active = active_instance(ref, timeline);
168 if (IS_ERR(active))
169 return PTR_ERR(active);
170
171 if (!i915_gem_active_isset(active))
172 ref->count++;
173 i915_gem_active_set(active, rq);
174
175 GEM_BUG_ON(!ref->count);
176 return 0;
177}
178
179bool i915_active_acquire(struct i915_active *ref)
180{
181 lockdep_assert_held(BKL(ref));
182 return !ref->count++;
183}
184
185void i915_active_release(struct i915_active *ref)
186{
187 lockdep_assert_held(BKL(ref));
188 __active_retire(ref);
189}
190
191int i915_active_wait(struct i915_active *ref)
192{
193 struct active_node *it, *n;
194 int ret = 0;
195
196 if (i915_active_acquire(ref))
197 goto out_release;
198
199 ret = i915_gem_active_retire(&ref->last, BKL(ref));
200 if (ret)
201 goto out_release;
202
203 rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
204 ret = i915_gem_active_retire(&it->base, BKL(ref));
205 if (ret)
206 break;
207 }
208
209out_release:
210 i915_active_release(ref);
211 return ret;
212}
213
214static int __i915_request_await_active(struct i915_request *rq,
215 struct i915_gem_active *active)
216{
217 struct i915_request *barrier =
218 i915_gem_active_raw(active, &rq->i915->drm.struct_mutex);
219
220 return barrier ? i915_request_await_dma_fence(rq, &barrier->fence) : 0;
221}
222
223int i915_request_await_active(struct i915_request *rq, struct i915_active *ref)
224{
225 struct active_node *it, *n;
226 int ret;
227
228 ret = __i915_request_await_active(rq, &ref->last);
229 if (ret)
230 return ret;
231
232 rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
233 ret = __i915_request_await_active(rq, &it->base);
234 if (ret)
235 return ret;
236 }
237
238 return 0;
239}
240
a42375af 241#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)
64d6c500
CW
242void i915_active_fini(struct i915_active *ref)
243{
64d6c500 244 GEM_BUG_ON(i915_gem_active_isset(&ref->last));
a42375af
CW
245 GEM_BUG_ON(!RB_EMPTY_ROOT(&ref->tree));
246 GEM_BUG_ON(ref->count);
64d6c500 247}
a42375af 248#endif
64d6c500
CW
249
250#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
251#include "selftests/i915_active.c"
252#endif
5f5c139d
CW
253
254int __init i915_global_active_init(void)
255{
256 global.slab_cache = KMEM_CACHE(active_node, SLAB_HWCACHE_ALIGN);
257 if (!global.slab_cache)
258 return -ENOMEM;
259
260 return 0;
261}
262
263void __exit i915_global_active_exit(void)
264{
265 kmem_cache_destroy(global.slab_cache);
266}