projects/linux·blob master · refs

362 lines · 9.4 KiB

1// SPDX-License-Identifier: GPL-2.0 OR MIT
2
3#include <drm/drm_exec.h>
4#include <drm/drm_gem.h>
5
6#include <linux/dma-resv.h>
7#include <linux/export.h>
8
9/**
10 * DOC: Overview
11 *
12 * This component mainly abstracts the retry loop necessary for locking
13 * multiple GEM objects while preparing hardware operations (e.g. command
14 * submissions, page table updates etc..).
15 *
16 * If a contention is detected while locking a GEM object the cleanup procedure
17 * unlocks all previously locked GEM objects and locks the contended one first
18 * before locking any further objects.
19 *
20 * After an object is locked fences slots can optionally be reserved on the
21 * dma_resv object inside the GEM object.
22 *
23 * A typical usage pattern should look like this::
24 *
25 * struct drm_gem_object *obj;
26 * struct drm_exec exec;
27 * int ret;
28 *
29 * drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT);
30 * drm_exec_until_all_locked(&exec) {
31 * ret = drm_exec_prepare_obj(&exec, boA, 1);
32 * drm_exec_retry_on_contention(&exec);
33 * if (ret)
34 * goto error;
35 *
36 * ret = drm_exec_prepare_obj(&exec, boB, 1);
37 * drm_exec_retry_on_contention(&exec);
38 * if (ret)
39 * goto error;
40 * }
41 *
42 * drm_exec_for_each_locked_object(&exec, obj) {
43 * dma_resv_add_fence(obj->resv, fence, DMA_RESV_USAGE_READ);
44 * ...
45 * }
46 * drm_exec_fini(&exec);
47 *
48 * See struct dma_exec for more details.
49 */
50
51/* Unlock and put the prelocked obj */
52static void drm_exec_drop_prelocked(struct drm_exec *exec)
53{
54 if (exec->prelocked) {
55 dma_resv_unlock(exec->prelocked->resv);
56 drm_gem_object_put(exec->prelocked);
57 exec->prelocked = NULL;
58 }
59}
60
61/* Unlock all objects and drop references */
62static void drm_exec_unlock_all(struct drm_exec *exec)
63{
64 struct drm_gem_object *obj;
65
66 drm_exec_for_each_locked_object_reverse(exec, obj) {
67 dma_resv_unlock(obj->resv);
68 drm_gem_object_put(obj);
69 }
70
71 drm_exec_drop_prelocked(exec);
72}
73
74/**
75 * drm_exec_init - initialize a drm_exec object
76 * @exec: the drm_exec object to initialize
77 * @flags: controls locking behavior, see DRM_EXEC_* defines
78 * @nr: the initial # of objects
79 *
80 * Initialize the object and make sure that we can track locked objects.
81 *
82 * If nr is non-zero then it is used as the initial objects table size.
83 * In either case, the table will grow (be re-allocated) on demand.
84 */
85void drm_exec_init(struct drm_exec *exec, u32 flags, unsigned nr)
86{
87 if (!nr)
88 nr = PAGE_SIZE / sizeof(void *);
89
90 exec->flags = flags;
91 exec->objects = kvmalloc_objs(*exec->objects, nr);
92
93 /* If allocation here fails, just delay that till the first use */
94 exec->max_objects = exec->objects ? nr : 0;
95 exec->num_objects = 0;
96 exec->contended = DRM_EXEC_DUMMY;
97 exec->prelocked = NULL;
98}
99EXPORT_SYMBOL(drm_exec_init);
100
101/**
102 * drm_exec_fini - finalize a drm_exec object
103 * @exec: the drm_exec object to finalize
104 *
105 * Unlock all locked objects, drop the references to objects and free all memory
106 * used for tracking the state.
107 */
108void drm_exec_fini(struct drm_exec *exec)
109{
110 drm_exec_unlock_all(exec);
111 kvfree(exec->objects);
112 if (exec->contended != DRM_EXEC_DUMMY) {
113 drm_gem_object_put(exec->contended);
114 ww_acquire_fini(&exec->ticket);
115 }
116}
117EXPORT_SYMBOL(drm_exec_fini);
118
119/**
120 * drm_exec_cleanup - cleanup when contention is detected
121 * @exec: the drm_exec object to cleanup
122 *
123 * Cleanup the current state and return true if we should stay inside the retry
124 * loop, false if there wasn't any contention detected and we can keep the
125 * objects locked.
126 */
127bool drm_exec_cleanup(struct drm_exec *exec)
128{
129 if (likely(!exec->contended)) {
130
131 /*
132 * The contended and prelocked object might never be passed to
133 * drm_exec_lock_obj() again. This can happen when the object
134 * is about to be cleaned up and we now hold the last reference
135 * to it. Make sure that we don't unnecessarily keep it around.
136 */
137 drm_exec_drop_prelocked(exec);
138 ww_acquire_done(&exec->ticket);
139 return false;
140 }
141
142 if (likely(exec->contended == DRM_EXEC_DUMMY)) {
143 exec->contended = NULL;
144 ww_acquire_init(&exec->ticket, &reservation_ww_class);
145 return true;
146 }
147
148 drm_exec_unlock_all(exec);
149 exec->num_objects = 0;
150 return true;
151}
152EXPORT_SYMBOL(drm_exec_cleanup);
153
154/* Track the locked object in the array */
155static int drm_exec_obj_locked(struct drm_exec *exec,
156 struct drm_gem_object *obj)
157{
158 if (unlikely(exec->num_objects == exec->max_objects)) {
159 size_t size = exec->max_objects * sizeof(void *);
160 void *tmp;
161
162 tmp = kvrealloc(exec->objects, size + PAGE_SIZE, GFP_KERNEL);
163 if (!tmp)
164 return -ENOMEM;
165
166 exec->objects = tmp;
167 exec->max_objects += PAGE_SIZE / sizeof(void *);
168 }
169 drm_gem_object_get(obj);
170 exec->objects[exec->num_objects++] = obj;
171
172 return 0;
173}
174
175/* Make sure the contended object is locked first */
176static int drm_exec_lock_contended(struct drm_exec *exec)
177{
178 struct drm_gem_object *obj = exec->contended;
179 int ret;
180
181 if (likely(!obj))
182 return 0;
183
184 /* Always cleanup the contention so that error handling can kick in */
185 exec->contended = NULL;
186 if (exec->flags & DRM_EXEC_INTERRUPTIBLE_WAIT) {
187 ret = dma_resv_lock_slow_interruptible(obj->resv,
188 &exec->ticket);
189 if (unlikely(ret))
190 goto error_dropref;
191 } else {
192 dma_resv_lock_slow(obj->resv, &exec->ticket);
193 }
194
195 /*
196 * It is perfectly possible that we see another contention before the
197 * first one is fully handled.
198 */
199 drm_exec_drop_prelocked(exec);
200
201 /* We move the reference from contended to prelocked here */
202 exec->prelocked = obj;
203 return 0;
204
205error_dropref:
206 drm_gem_object_put(obj);
207 return ret;
208}
209
210/**
211 * drm_exec_lock_obj - lock a GEM object for use
212 * @exec: the drm_exec object with the state
213 * @obj: the GEM object to lock
214 *
215 * Lock a GEM object for use and grab a reference to it.
216 *
217 * Returns: -EDEADLK if a contention is detected, -EALREADY when object is
218 * already locked (can be suppressed by setting the DRM_EXEC_IGNORE_DUPLICATES
219 * flag), -ENOMEM when memory allocation failed and zero for success.
220 */
221int drm_exec_lock_obj(struct drm_exec *exec, struct drm_gem_object *obj)
222{
223 int ret;
224
225 ret = drm_exec_lock_contended(exec);
226 if (unlikely(ret))
227 return ret;
228
229 if (exec->prelocked == obj) {
230 drm_gem_object_put(exec->prelocked);
231 exec->prelocked = NULL;
232 } else {
233 if (exec->flags & DRM_EXEC_INTERRUPTIBLE_WAIT)
234 ret = dma_resv_lock_interruptible(obj->resv,
235 &exec->ticket);
236 else
237 ret = dma_resv_lock(obj->resv, &exec->ticket);
238
239 if (unlikely(ret == -EDEADLK)) {
240 drm_gem_object_get(obj);
241 exec->contended = obj;
242 return -EDEADLK;
243 }
244
245 if (unlikely(ret == -EALREADY) &&
246 exec->flags & DRM_EXEC_IGNORE_DUPLICATES)
247 return 0;
248
249 if (unlikely(ret))
250 return ret;
251 }
252
253 ret = drm_exec_obj_locked(exec, obj);
254 if (ret)
255 dma_resv_unlock(obj->resv);
256
257 return ret;
258}
259EXPORT_SYMBOL(drm_exec_lock_obj);
260
261/**
262 * drm_exec_unlock_obj - unlock a GEM object in this exec context
263 * @exec: the drm_exec object with the state
264 * @obj: the GEM object to unlock
265 *
266 * Unlock the GEM object and remove it from the collection of locked objects.
267 * Should only be used to unlock the most recently locked objects. It's not time
268 * efficient to unlock objects locked long ago.
269 */
270void drm_exec_unlock_obj(struct drm_exec *exec, struct drm_gem_object *obj)
271{
272 unsigned int i;
273
274 for (i = exec->num_objects; i--;) {
275 if (exec->objects[i] == obj) {
276 dma_resv_unlock(obj->resv);
277 for (++i; i < exec->num_objects; ++i)
278 exec->objects[i - 1] = exec->objects[i];
279 --exec->num_objects;
280 drm_gem_object_put(obj);
281 return;
282 }
283
284 }
285}
286EXPORT_SYMBOL(drm_exec_unlock_obj);
287
288/**
289 * drm_exec_prepare_obj - prepare a GEM object for use
290 * @exec: the drm_exec object with the state
291 * @obj: the GEM object to prepare
292 * @num_fences: how many fences to reserve
293 *
294 * Prepare a GEM object for use by locking it and reserving fence slots.
295 *
296 * Returns: -EDEADLK if a contention is detected, -EALREADY when object is
297 * already locked, -ENOMEM when memory allocation failed and zero for success.
298 */
299int drm_exec_prepare_obj(struct drm_exec *exec, struct drm_gem_object *obj,
300 unsigned int num_fences)
301{
302 int ret;
303
304 ret = drm_exec_lock_obj(exec, obj);
305 if (ret)
306 return ret;
307
308 ret = dma_resv_reserve_fences(obj->resv, num_fences);
309 if (ret) {
310 drm_exec_unlock_obj(exec, obj);
311 return ret;
312 }
313
314 return 0;
315}
316EXPORT_SYMBOL(drm_exec_prepare_obj);
317
318/**
319 * drm_exec_prepare_array - helper to prepare an array of objects
320 * @exec: the drm_exec object with the state
321 * @objects: array of GEM object to prepare
322 * @num_objects: number of GEM objects in the array
323 * @num_fences: number of fences to reserve on each GEM object
324 *
325 * Prepares all GEM objects in an array, aborts on first error.
326 * Reserves @num_fences on each GEM object after locking it.
327 *
328 * Returns: -EDEADLOCK on contention, -EALREADY when object is already locked,
329 * -ENOMEM when memory allocation failed and zero for success.
330 */
331int drm_exec_prepare_array(struct drm_exec *exec,
332 struct drm_gem_object **objects,
333 unsigned int num_objects,
334 unsigned int num_fences)
335{
336 int ret;
337
338 /*
339 * Make sure to lock a contended object even when no objects are
340 * given, otherwise drm_exec_retry_on_contention() would loop
341 * forever on patterns like:
342 *
343 * ret = drm_exec_prepare_array(exec, objs, num_objects, ...);
344 * drm_exec_retry_on_contention(exec);
345 *
346 * with num_objects == 0.
347 */
348 if (!num_objects)
349 return drm_exec_lock_contended(exec);
350
351 for (unsigned int i = 0; i < num_objects; ++i) {
352 ret = drm_exec_prepare_obj(exec, objects[i], num_fences);
353 if (unlikely(ret))
354 return ret;
355 }
356
357 return 0;
358}
359EXPORT_SYMBOL(drm_exec_prepare_array);
360
361MODULE_DESCRIPTION("DRM execution context");
362MODULE_LICENSE("Dual MIT/GPL");