aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/drm_syncobj.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/drm_syncobj.c')
-rw-r--r--drivers/gpu/drm/drm_syncobj.c359
1 files changed, 286 insertions, 73 deletions
diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c
index 5c2091dbd230..da8175d9c6ff 100644
--- a/drivers/gpu/drm/drm_syncobj.c
+++ b/drivers/gpu/drm/drm_syncobj.c
@@ -56,6 +56,9 @@
56#include "drm_internal.h" 56#include "drm_internal.h"
57#include <drm/drm_syncobj.h> 57#include <drm/drm_syncobj.h>
58 58
59/* merge normal syncobj to timeline syncobj, the point interval is 1 */
60#define DRM_SYNCOBJ_BINARY_POINT 1
61
59struct drm_syncobj_stub_fence { 62struct drm_syncobj_stub_fence {
60 struct dma_fence base; 63 struct dma_fence base;
61 spinlock_t lock; 64 spinlock_t lock;
@@ -71,7 +74,29 @@ static const struct dma_fence_ops drm_syncobj_stub_fence_ops = {
71 .get_timeline_name = drm_syncobj_stub_fence_get_name, 74 .get_timeline_name = drm_syncobj_stub_fence_get_name,
72}; 75};
73 76
77struct drm_syncobj_signal_pt {
78 struct dma_fence_array *fence_array;
79 u64 value;
80 struct list_head list;
81};
82
83static DEFINE_SPINLOCK(signaled_fence_lock);
84static struct dma_fence signaled_fence;
74 85
86static struct dma_fence *drm_syncobj_get_stub_fence(void)
87{
88 spin_lock(&signaled_fence_lock);
89 if (!signaled_fence.ops) {
90 dma_fence_init(&signaled_fence,
91 &drm_syncobj_stub_fence_ops,
92 &signaled_fence_lock,
93 0, 0);
94 dma_fence_signal_locked(&signaled_fence);
95 }
96 spin_unlock(&signaled_fence_lock);
97
98 return dma_fence_get(&signaled_fence);
99}
75/** 100/**
76 * drm_syncobj_find - lookup and reference a sync object. 101 * drm_syncobj_find - lookup and reference a sync object.
77 * @file_private: drm file private pointer 102 * @file_private: drm file private pointer
@@ -98,6 +123,27 @@ struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private,
98} 123}
99EXPORT_SYMBOL(drm_syncobj_find); 124EXPORT_SYMBOL(drm_syncobj_find);
100 125
126static struct dma_fence *
127drm_syncobj_find_signal_pt_for_point(struct drm_syncobj *syncobj,
128 uint64_t point)
129{
130 struct drm_syncobj_signal_pt *signal_pt;
131
132 if ((syncobj->type == DRM_SYNCOBJ_TYPE_TIMELINE) &&
133 (point <= syncobj->timeline))
134 return drm_syncobj_get_stub_fence();
135
136 list_for_each_entry(signal_pt, &syncobj->signal_pt_list, list) {
137 if (point > signal_pt->value)
138 continue;
139 if ((syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) &&
140 (point != signal_pt->value))
141 continue;
142 return dma_fence_get(&signal_pt->fence_array->base);
143 }
144 return NULL;
145}
146
101static void drm_syncobj_add_callback_locked(struct drm_syncobj *syncobj, 147static void drm_syncobj_add_callback_locked(struct drm_syncobj *syncobj,
102 struct drm_syncobj_cb *cb, 148 struct drm_syncobj_cb *cb,
103 drm_syncobj_func_t func) 149 drm_syncobj_func_t func)
@@ -106,55 +152,158 @@ static void drm_syncobj_add_callback_locked(struct drm_syncobj *syncobj,
106 list_add_tail(&cb->node, &syncobj->cb_list); 152 list_add_tail(&cb->node, &syncobj->cb_list);
107} 153}
108 154
109static int drm_syncobj_fence_get_or_add_callback(struct drm_syncobj *syncobj, 155static void drm_syncobj_fence_get_or_add_callback(struct drm_syncobj *syncobj,
110 struct dma_fence **fence, 156 struct dma_fence **fence,
111 struct drm_syncobj_cb *cb, 157 struct drm_syncobj_cb *cb,
112 drm_syncobj_func_t func) 158 drm_syncobj_func_t func)
113{ 159{
114 int ret; 160 u64 pt_value = 0;
115 161
116 WARN_ON(*fence); 162 WARN_ON(*fence);
117 163
118 *fence = drm_syncobj_fence_get(syncobj); 164 if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) {
119 if (*fence) 165 /*BINARY syncobj always wait on last pt */
120 return 1; 166 pt_value = syncobj->signal_point;
121 167
122 spin_lock(&syncobj->lock); 168 if (pt_value == 0)
123 /* We've already tried once to get a fence and failed. Now that we 169 pt_value += DRM_SYNCOBJ_BINARY_POINT;
124 * have the lock, try one more time just to be sure we don't add a
125 * callback when a fence has already been set.
126 */
127 if (syncobj->fence) {
128 *fence = dma_fence_get(rcu_dereference_protected(syncobj->fence,
129 lockdep_is_held(&syncobj->lock)));
130 ret = 1;
131 } else {
132 *fence = NULL;
133 drm_syncobj_add_callback_locked(syncobj, cb, func);
134 ret = 0;
135 } 170 }
136 spin_unlock(&syncobj->lock);
137 171
138 return ret; 172 mutex_lock(&syncobj->cb_mutex);
173 spin_lock(&syncobj->pt_lock);
174 *fence = drm_syncobj_find_signal_pt_for_point(syncobj, pt_value);
175 spin_unlock(&syncobj->pt_lock);
176 if (!*fence)
177 drm_syncobj_add_callback_locked(syncobj, cb, func);
178 mutex_unlock(&syncobj->cb_mutex);
139} 179}
140 180
141void drm_syncobj_add_callback(struct drm_syncobj *syncobj, 181static void drm_syncobj_remove_callback(struct drm_syncobj *syncobj,
142 struct drm_syncobj_cb *cb, 182 struct drm_syncobj_cb *cb)
143 drm_syncobj_func_t func)
144{ 183{
145 spin_lock(&syncobj->lock); 184 mutex_lock(&syncobj->cb_mutex);
146 drm_syncobj_add_callback_locked(syncobj, cb, func); 185 list_del_init(&cb->node);
147 spin_unlock(&syncobj->lock); 186 mutex_unlock(&syncobj->cb_mutex);
148} 187}
149 188
150void drm_syncobj_remove_callback(struct drm_syncobj *syncobj, 189static void drm_syncobj_init(struct drm_syncobj *syncobj)
151 struct drm_syncobj_cb *cb)
152{ 190{
153 spin_lock(&syncobj->lock); 191 spin_lock(&syncobj->pt_lock);
154 list_del_init(&cb->node); 192 syncobj->timeline_context = dma_fence_context_alloc(1);
155 spin_unlock(&syncobj->lock); 193 syncobj->timeline = 0;
194 syncobj->signal_point = 0;
195 init_waitqueue_head(&syncobj->wq);
196
197 INIT_LIST_HEAD(&syncobj->signal_pt_list);
198 spin_unlock(&syncobj->pt_lock);
199}
200
201static void drm_syncobj_fini(struct drm_syncobj *syncobj)
202{
203 struct drm_syncobj_signal_pt *signal_pt = NULL, *tmp;
204
205 spin_lock(&syncobj->pt_lock);
206 list_for_each_entry_safe(signal_pt, tmp,
207 &syncobj->signal_pt_list, list) {
208 list_del(&signal_pt->list);
209 dma_fence_put(&signal_pt->fence_array->base);
210 kfree(signal_pt);
211 }
212 spin_unlock(&syncobj->pt_lock);
213}
214
215static int drm_syncobj_create_signal_pt(struct drm_syncobj *syncobj,
216 struct dma_fence *fence,
217 u64 point)
218{
219 struct drm_syncobj_signal_pt *signal_pt =
220 kzalloc(sizeof(struct drm_syncobj_signal_pt), GFP_KERNEL);
221 struct drm_syncobj_signal_pt *tail_pt;
222 struct dma_fence **fences;
223 int num_fences = 0;
224 int ret = 0, i;
225
226 if (!signal_pt)
227 return -ENOMEM;
228 if (!fence)
229 goto out;
230
231 fences = kmalloc_array(sizeof(void *), 2, GFP_KERNEL);
232 if (!fences) {
233 ret = -ENOMEM;
234 goto out;
235 }
236 fences[num_fences++] = dma_fence_get(fence);
237 /* timeline syncobj must take this dependency */
238 if (syncobj->type == DRM_SYNCOBJ_TYPE_TIMELINE) {
239 spin_lock(&syncobj->pt_lock);
240 if (!list_empty(&syncobj->signal_pt_list)) {
241 tail_pt = list_last_entry(&syncobj->signal_pt_list,
242 struct drm_syncobj_signal_pt, list);
243 fences[num_fences++] =
244 dma_fence_get(&tail_pt->fence_array->base);
245 }
246 spin_unlock(&syncobj->pt_lock);
247 }
248 signal_pt->fence_array = dma_fence_array_create(num_fences, fences,
249 syncobj->timeline_context,
250 point, false);
251 if (!signal_pt->fence_array) {
252 ret = -ENOMEM;
253 goto fail;
254 }
255
256 spin_lock(&syncobj->pt_lock);
257 if (syncobj->signal_point >= point) {
258 DRM_WARN("A later signal is ready!");
259 spin_unlock(&syncobj->pt_lock);
260 goto exist;
261 }
262 signal_pt->value = point;
263 list_add_tail(&signal_pt->list, &syncobj->signal_pt_list);
264 syncobj->signal_point = point;
265 spin_unlock(&syncobj->pt_lock);
266 wake_up_all(&syncobj->wq);
267
268 return 0;
269exist:
270 dma_fence_put(&signal_pt->fence_array->base);
271fail:
272 for (i = 0; i < num_fences; i++)
273 dma_fence_put(fences[i]);
274 kfree(fences);
275out:
276 kfree(signal_pt);
277 return ret;
156} 278}
157 279
280static void drm_syncobj_garbage_collection(struct drm_syncobj *syncobj)
281{
282 struct drm_syncobj_signal_pt *signal_pt, *tmp, *tail_pt;
283
284 spin_lock(&syncobj->pt_lock);
285 tail_pt = list_last_entry(&syncobj->signal_pt_list,
286 struct drm_syncobj_signal_pt,
287 list);
288 list_for_each_entry_safe(signal_pt, tmp,
289 &syncobj->signal_pt_list, list) {
290 if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY &&
291 signal_pt == tail_pt)
292 continue;
293 if (dma_fence_is_signaled(&signal_pt->fence_array->base)) {
294 syncobj->timeline = signal_pt->value;
295 list_del(&signal_pt->list);
296 dma_fence_put(&signal_pt->fence_array->base);
297 kfree(signal_pt);
298 } else {
299 /*signal_pt is in order in list, from small to big, so
300 * the later must not be signal either */
301 break;
302 }
303 }
304
305 spin_unlock(&syncobj->pt_lock);
306}
158/** 307/**
159 * drm_syncobj_replace_fence - replace fence in a sync object. 308 * drm_syncobj_replace_fence - replace fence in a sync object.
160 * @syncobj: Sync object to replace fence in 309 * @syncobj: Sync object to replace fence in
@@ -167,28 +316,30 @@ void drm_syncobj_replace_fence(struct drm_syncobj *syncobj,
167 u64 point, 316 u64 point,
168 struct dma_fence *fence) 317 struct dma_fence *fence)
169{ 318{
170 struct dma_fence *old_fence; 319 u64 pt_value = point;
171 struct drm_syncobj_cb *cur, *tmp; 320
172 321 drm_syncobj_garbage_collection(syncobj);
173 if (fence) 322 if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) {
174 dma_fence_get(fence); 323 if (!fence) {
175 324 drm_syncobj_fini(syncobj);
176 spin_lock(&syncobj->lock); 325 drm_syncobj_init(syncobj);
177 326 return;
178 old_fence = rcu_dereference_protected(syncobj->fence, 327 }
179 lockdep_is_held(&syncobj->lock)); 328 pt_value = syncobj->signal_point +
180 rcu_assign_pointer(syncobj->fence, fence); 329 DRM_SYNCOBJ_BINARY_POINT;
330 }
331 drm_syncobj_create_signal_pt(syncobj, fence, pt_value);
332 if (fence) {
333 struct drm_syncobj_cb *cur, *tmp;
334 LIST_HEAD(cb_list);
181 335
182 if (fence != old_fence) { 336 mutex_lock(&syncobj->cb_mutex);
183 list_for_each_entry_safe(cur, tmp, &syncobj->cb_list, node) { 337 list_for_each_entry_safe(cur, tmp, &syncobj->cb_list, node) {
184 list_del_init(&cur->node); 338 list_del_init(&cur->node);
185 cur->func(syncobj, cur); 339 cur->func(syncobj, cur);
186 } 340 }
341 mutex_unlock(&syncobj->cb_mutex);
187 } 342 }
188
189 spin_unlock(&syncobj->lock);
190
191 dma_fence_put(old_fence);
192} 343}
193EXPORT_SYMBOL(drm_syncobj_replace_fence); 344EXPORT_SYMBOL(drm_syncobj_replace_fence);
194 345
@@ -211,35 +362,89 @@ static int drm_syncobj_assign_null_handle(struct drm_syncobj *syncobj)
211 return 0; 362 return 0;
212} 363}
213 364
365static int
366drm_syncobj_point_get(struct drm_syncobj *syncobj, u64 point, u64 flags,
367 struct dma_fence **fence)
368{
369 int ret = 0;
370
371 if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) {
372 ret = wait_event_interruptible(syncobj->wq,
373 point <= syncobj->signal_point);
374 if (ret < 0)
375 return ret;
376 }
377 spin_lock(&syncobj->pt_lock);
378 *fence = drm_syncobj_find_signal_pt_for_point(syncobj, point);
379 if (!*fence)
380 ret = -EINVAL;
381 spin_unlock(&syncobj->pt_lock);
382 return ret;
383}
384
385/**
386 * drm_syncobj_search_fence - lookup and reference the fence in a sync object or
387 * in a timeline point
388 * @syncobj: sync object pointer
389 * @point: timeline point
390 * @flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or not
391 * @fence: out parameter for the fence
392 *
393 * if flags is DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT, the function will block
394 * here until specific timeline points is reached.
395 * if not, you need a submit thread and block in userspace until all future
396 * timeline points have materialized, only then you can submit to the kernel,
397 * otherwise, function will fail to return fence.
398 *
399 * Returns 0 on success or a negative error value on failure. On success @fence
400 * contains a reference to the fence, which must be released by calling
401 * dma_fence_put().
402 */
403int drm_syncobj_search_fence(struct drm_syncobj *syncobj, u64 point,
404 u64 flags, struct dma_fence **fence)
405{
406 u64 pt_value = point;
407
408 if (!syncobj)
409 return -ENOENT;
410
411 drm_syncobj_garbage_collection(syncobj);
412 if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) {
413 /*BINARY syncobj always wait on last pt */
414 pt_value = syncobj->signal_point;
415
416 if (pt_value == 0)
417 pt_value += DRM_SYNCOBJ_BINARY_POINT;
418 }
419 return drm_syncobj_point_get(syncobj, pt_value, flags, fence);
420}
421EXPORT_SYMBOL(drm_syncobj_search_fence);
422
214/** 423/**
215 * drm_syncobj_find_fence - lookup and reference the fence in a sync object 424 * drm_syncobj_find_fence - lookup and reference the fence in a sync object
216 * @file_private: drm file private pointer 425 * @file_private: drm file private pointer
217 * @handle: sync object handle to lookup. 426 * @handle: sync object handle to lookup.
218 * @point: timeline point 427 * @point: timeline point
428 * @flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or not
219 * @fence: out parameter for the fence 429 * @fence: out parameter for the fence
220 * 430 *
221 * This is just a convenience function that combines drm_syncobj_find() and 431 * This is just a convenience function that combines drm_syncobj_find() and
222 * drm_syncobj_fence_get(). 432 * drm_syncobj_lookup_fence().
223 * 433 *
224 * Returns 0 on success or a negative error value on failure. On success @fence 434 * Returns 0 on success or a negative error value on failure. On success @fence
225 * contains a reference to the fence, which must be released by calling 435 * contains a reference to the fence, which must be released by calling
226 * dma_fence_put(). 436 * dma_fence_put().
227 */ 437 */
228int drm_syncobj_find_fence(struct drm_file *file_private, 438int drm_syncobj_find_fence(struct drm_file *file_private,
229 u32 handle, u64 point, 439 u32 handle, u64 point, u64 flags,
230 struct dma_fence **fence) 440 struct dma_fence **fence)
231{ 441{
232 struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle); 442 struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);
233 int ret = 0; 443 int ret;
234
235 if (!syncobj)
236 return -ENOENT;
237 444
238 *fence = drm_syncobj_fence_get(syncobj); 445 ret = drm_syncobj_search_fence(syncobj, point, flags, fence);
239 if (!*fence) { 446 if (syncobj)
240 ret = -EINVAL; 447 drm_syncobj_put(syncobj);
241 }
242 drm_syncobj_put(syncobj);
243 return ret; 448 return ret;
244} 449}
245EXPORT_SYMBOL(drm_syncobj_find_fence); 450EXPORT_SYMBOL(drm_syncobj_find_fence);
@@ -255,7 +460,7 @@ void drm_syncobj_free(struct kref *kref)
255 struct drm_syncobj *syncobj = container_of(kref, 460 struct drm_syncobj *syncobj = container_of(kref,
256 struct drm_syncobj, 461 struct drm_syncobj,
257 refcount); 462 refcount);
258 drm_syncobj_replace_fence(syncobj, 0, NULL); 463 drm_syncobj_fini(syncobj);
259 kfree(syncobj); 464 kfree(syncobj);
260} 465}
261EXPORT_SYMBOL(drm_syncobj_free); 466EXPORT_SYMBOL(drm_syncobj_free);
@@ -284,7 +489,13 @@ int drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags,
284 489
285 kref_init(&syncobj->refcount); 490 kref_init(&syncobj->refcount);
286 INIT_LIST_HEAD(&syncobj->cb_list); 491 INIT_LIST_HEAD(&syncobj->cb_list);
287 spin_lock_init(&syncobj->lock); 492 spin_lock_init(&syncobj->pt_lock);
493 mutex_init(&syncobj->cb_mutex);
494 if (flags & DRM_SYNCOBJ_CREATE_TYPE_TIMELINE)
495 syncobj->type = DRM_SYNCOBJ_TYPE_TIMELINE;
496 else
497 syncobj->type = DRM_SYNCOBJ_TYPE_BINARY;
498 drm_syncobj_init(syncobj);
288 499
289 if (flags & DRM_SYNCOBJ_CREATE_SIGNALED) { 500 if (flags & DRM_SYNCOBJ_CREATE_SIGNALED) {
290 ret = drm_syncobj_assign_null_handle(syncobj); 501 ret = drm_syncobj_assign_null_handle(syncobj);
@@ -497,7 +708,7 @@ static int drm_syncobj_export_sync_file(struct drm_file *file_private,
497 if (fd < 0) 708 if (fd < 0)
498 return fd; 709 return fd;
499 710
500 ret = drm_syncobj_find_fence(file_private, handle, 0, &fence); 711 ret = drm_syncobj_find_fence(file_private, handle, 0, 0, &fence);
501 if (ret) 712 if (ret)
502 goto err_put_fd; 713 goto err_put_fd;
503 714
@@ -567,7 +778,8 @@ drm_syncobj_create_ioctl(struct drm_device *dev, void *data,
567 return -EOPNOTSUPP; 778 return -EOPNOTSUPP;
568 779
569 /* no valid flags yet */ 780 /* no valid flags yet */
570 if (args->flags & ~DRM_SYNCOBJ_CREATE_SIGNALED) 781 if (args->flags & ~(DRM_SYNCOBJ_CREATE_SIGNALED |
782 DRM_SYNCOBJ_CREATE_TYPE_TIMELINE))
571 return -EINVAL; 783 return -EINVAL;
572 784
573 return drm_syncobj_create_as_handle(file_private, 785 return drm_syncobj_create_as_handle(file_private,
@@ -660,9 +872,8 @@ static void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj,
660 struct syncobj_wait_entry *wait = 872 struct syncobj_wait_entry *wait =
661 container_of(cb, struct syncobj_wait_entry, syncobj_cb); 873 container_of(cb, struct syncobj_wait_entry, syncobj_cb);
662 874
663 /* This happens inside the syncobj lock */ 875 drm_syncobj_search_fence(syncobj, 0, 0, &wait->fence);
664 wait->fence = dma_fence_get(rcu_dereference_protected(syncobj->fence, 876
665 lockdep_is_held(&syncobj->lock)));
666 wake_up_process(wait->task); 877 wake_up_process(wait->task);
667} 878}
668 879
@@ -688,7 +899,8 @@ static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs,
688 signaled_count = 0; 899 signaled_count = 0;
689 for (i = 0; i < count; ++i) { 900 for (i = 0; i < count; ++i) {
690 entries[i].task = current; 901 entries[i].task = current;
691 entries[i].fence = drm_syncobj_fence_get(syncobjs[i]); 902 drm_syncobj_search_fence(syncobjs[i], 0, 0,
903 &entries[i].fence);
692 if (!entries[i].fence) { 904 if (!entries[i].fence) {
693 if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) { 905 if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) {
694 continue; 906 continue;
@@ -953,12 +1165,13 @@ drm_syncobj_reset_ioctl(struct drm_device *dev, void *data,
953 if (ret < 0) 1165 if (ret < 0)
954 return ret; 1166 return ret;
955 1167
956 for (i = 0; i < args->count_handles; i++) 1168 for (i = 0; i < args->count_handles; i++) {
957 drm_syncobj_replace_fence(syncobjs[i], 0, NULL); 1169 drm_syncobj_fini(syncobjs[i]);
958 1170 drm_syncobj_init(syncobjs[i]);
1171 }
959 drm_syncobj_array_free(syncobjs, args->count_handles); 1172 drm_syncobj_array_free(syncobjs, args->count_handles);
960 1173
961 return 0; 1174 return ret;
962} 1175}
963 1176
964int 1177int