drm/amd/powerplay: modify smu_update_table to use SMU_TABLE_xxx as the input
[linux-2.6-block.git] / drivers / gpu / drm / drm_syncobj.c
CommitLineData
e9083420
DA
1/*
2 * Copyright 2017 Red Hat
5e60a10e
DA
3 * Parts ported from amdgpu (fence wait code).
4 * Copyright 2016 Advanced Micro Devices, Inc.
e9083420
DA
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 * IN THE SOFTWARE.
24 *
25 * Authors:
26 *
27 */
28
29/**
30 * DOC: Overview
31 *
924fe8df
DV
32 * DRM synchronisation objects (syncobj, see struct &drm_syncobj) are
33 * persistent objects that contain an optional fence. The fence can be updated
34 * with a new fence, or be NULL.
e9083420 35 *
5e60a10e
DA
36 * syncobj's can be waited upon, where it will wait for the underlying
37 * fence.
38 *
e9083420
DA
39 * syncobj's can be export to fd's and back, these fd's are opaque and
40 * have no other use case, except passing the syncobj between processes.
41 *
42 * Their primary use-case is to implement Vulkan fences and semaphores.
43 *
44 * syncobj have a kref reference count, but also have an optional file.
45 * The file is only created once the syncobj is exported.
46 * The file takes a reference on the kref.
47 */
48
0500c04e 49#include <linux/anon_inodes.h>
e9083420
DA
50#include <linux/file.h>
51#include <linux/fs.h>
e7aca503 52#include <linux/sched/signal.h>
0500c04e
SR
53#include <linux/sync_file.h>
54#include <linux/uaccess.h>
e9083420 55
0500c04e
SR
56#include <drm/drm_drv.h>
57#include <drm/drm_file.h>
58#include <drm/drm_gem.h>
59#include <drm/drm_print.h>
e9083420
DA
60#include <drm/drm_syncobj.h>
61
0500c04e
SR
62#include "drm_internal.h"
63
61a98b1b
CK
64struct syncobj_wait_entry {
65 struct list_head node;
66 struct task_struct *task;
67 struct dma_fence *fence;
68 struct dma_fence_cb fence_cb;
01d6c357 69 u64 point;
61a98b1b
CK
70};
71
72static void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj,
73 struct syncobj_wait_entry *wait);
74
e9083420
DA
75/**
76 * drm_syncobj_find - lookup and reference a sync object.
77 * @file_private: drm file private pointer
78 * @handle: sync object handle to lookup.
79 *
924fe8df
DV
80 * Returns a reference to the syncobj pointed to by handle or NULL. The
81 * reference must be released by calling drm_syncobj_put().
e9083420
DA
82 */
83struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private,
84 u32 handle)
85{
86 struct drm_syncobj *syncobj;
87
88 spin_lock(&file_private->syncobj_table_lock);
89
90 /* Check if we currently have a reference on the object */
91 syncobj = idr_find(&file_private->syncobj_idr, handle);
92 if (syncobj)
93 drm_syncobj_get(syncobj);
94
95 spin_unlock(&file_private->syncobj_table_lock);
96
97 return syncobj;
98}
99EXPORT_SYMBOL(drm_syncobj_find);
100
61a98b1b
CK
101static void drm_syncobj_fence_add_wait(struct drm_syncobj *syncobj,
102 struct syncobj_wait_entry *wait)
9c19fb10 103{
01d6c357
CZ
104 struct dma_fence *fence;
105
61a98b1b
CK
106 if (wait->fence)
107 return;
43cf1fc0 108
131280a1
EA
109 spin_lock(&syncobj->lock);
110 /* We've already tried once to get a fence and failed. Now that we
111 * have the lock, try one more time just to be sure we don't add a
112 * callback when a fence has already been set.
113 */
01d6c357
CZ
114 fence = dma_fence_get(rcu_dereference_protected(syncobj->fence, 1));
115 if (!fence || dma_fence_chain_find_seqno(&fence, wait->point)) {
116 dma_fence_put(fence);
61a98b1b 117 list_add_tail(&wait->node, &syncobj->cb_list);
01d6c357
CZ
118 } else if (!fence) {
119 wait->fence = dma_fence_get_stub();
120 } else {
121 wait->fence = fence;
122 }
131280a1 123 spin_unlock(&syncobj->lock);
48197bc5
CZ
124}
125
61a98b1b
CK
126static void drm_syncobj_remove_wait(struct drm_syncobj *syncobj,
127 struct syncobj_wait_entry *wait)
48197bc5 128{
61a98b1b
CK
129 if (!wait->node.next)
130 return;
48197bc5 131
131280a1 132 spin_lock(&syncobj->lock);
61a98b1b 133 list_del_init(&wait->node);
131280a1 134 spin_unlock(&syncobj->lock);
48197bc5
CZ
135}
136
44f8a139
CK
137/**
138 * drm_syncobj_add_point - add new timeline point to the syncobj
139 * @syncobj: sync object to add timeline point do
140 * @chain: chain node to use to add the point
141 * @fence: fence to encapsulate in the chain node
142 * @point: sequence number to use for the point
143 *
144 * Add the chain node as new timeline point to the syncobj.
145 */
146void drm_syncobj_add_point(struct drm_syncobj *syncobj,
147 struct dma_fence_chain *chain,
148 struct dma_fence *fence,
149 uint64_t point)
150{
151 struct syncobj_wait_entry *cur, *tmp;
152 struct dma_fence *prev;
153
154 dma_fence_get(fence);
155
156 spin_lock(&syncobj->lock);
157
158 prev = drm_syncobj_fence_get(syncobj);
159 /* You are adding an unorder point to timeline, which could cause payload returned from query_ioctl is 0! */
160 if (prev && prev->seqno >= point)
161 DRM_ERROR("You are adding an unorder point to timeline!\n");
162 dma_fence_chain_init(chain, prev, fence, point);
163 rcu_assign_pointer(syncobj->fence, &chain->base);
164
01d6c357 165 list_for_each_entry_safe(cur, tmp, &syncobj->cb_list, node)
44f8a139 166 syncobj_wait_syncobj_func(syncobj, cur);
44f8a139
CK
167 spin_unlock(&syncobj->lock);
168
169 /* Walk the chain once to trigger garbage collection */
170 dma_fence_chain_for_each(fence, prev);
171 dma_fence_put(prev);
172}
173EXPORT_SYMBOL(drm_syncobj_add_point);
174
e9083420
DA
175/**
176 * drm_syncobj_replace_fence - replace fence in a sync object.
e9083420
DA
177 * @syncobj: Sync object to replace fence in
178 * @fence: fence to install in sync file.
179 *
0b258ed1 180 * This replaces the fence on a sync object.
e9083420 181 */
00fc2c26 182void drm_syncobj_replace_fence(struct drm_syncobj *syncobj,
e9083420
DA
183 struct dma_fence *fence)
184{
131280a1 185 struct dma_fence *old_fence;
61a98b1b 186 struct syncobj_wait_entry *cur, *tmp;
131280a1
EA
187
188 if (fence)
189 dma_fence_get(fence);
190
191 spin_lock(&syncobj->lock);
192
193 old_fence = rcu_dereference_protected(syncobj->fence,
194 lockdep_is_held(&syncobj->lock));
195 rcu_assign_pointer(syncobj->fence, fence);
9c19fb10 196
131280a1 197 if (fence != old_fence) {
01d6c357 198 list_for_each_entry_safe(cur, tmp, &syncobj->cb_list, node)
61a98b1b 199 syncobj_wait_syncobj_func(syncobj, cur);
9c19fb10 200 }
131280a1
EA
201
202 spin_unlock(&syncobj->lock);
203
204 dma_fence_put(old_fence);
e9083420
DA
205}
206EXPORT_SYMBOL(drm_syncobj_replace_fence);
207
86bbd89d
CK
208/**
209 * drm_syncobj_assign_null_handle - assign a stub fence to the sync object
210 * @syncobj: sync object to assign the fence on
211 *
212 * Assign a already signaled stub fence to the sync object.
213 */
214static void drm_syncobj_assign_null_handle(struct drm_syncobj *syncobj)
1fc08218 215{
86bbd89d 216 struct dma_fence *fence = dma_fence_get_stub();
1fc08218 217
0b258ed1 218 drm_syncobj_replace_fence(syncobj, fence);
86bbd89d 219 dma_fence_put(fence);
1fc08218
JE
220}
221
bc9c80fe
CK
222/* 5s default for wait submission */
223#define DRM_SYNCOBJ_WAIT_FOR_SUBMIT_TIMEOUT 5000000000ULL
924fe8df
DV
224/**
225 * drm_syncobj_find_fence - lookup and reference the fence in a sync object
226 * @file_private: drm file private pointer
227 * @handle: sync object handle to lookup.
0a6730ea 228 * @point: timeline point
871edc96 229 * @flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or not
924fe8df
DV
230 * @fence: out parameter for the fence
231 *
232 * This is just a convenience function that combines drm_syncobj_find() and
131280a1 233 * drm_syncobj_fence_get().
924fe8df
DV
234 *
235 * Returns 0 on success or a negative error value on failure. On success @fence
236 * contains a reference to the fence, which must be released by calling
237 * dma_fence_put().
238 */
afaf5923 239int drm_syncobj_find_fence(struct drm_file *file_private,
649fdce2 240 u32 handle, u64 point, u64 flags,
afaf5923 241 struct dma_fence **fence)
e9083420
DA
242{
243 struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);
bc9c80fe
CK
244 struct syncobj_wait_entry wait;
245 u64 timeout = nsecs_to_jiffies64(DRM_SYNCOBJ_WAIT_FOR_SUBMIT_TIMEOUT);
246 int ret;
e9083420 247
131280a1
EA
248 if (!syncobj)
249 return -ENOENT;
250
251 *fence = drm_syncobj_fence_get(syncobj);
bc9c80fe
CK
252 drm_syncobj_put(syncobj);
253
254 if (*fence) {
255 ret = dma_fence_chain_find_seqno(fence, point);
256 if (!ret)
257 return 0;
258 dma_fence_put(*fence);
259 } else {
131280a1
EA
260 ret = -EINVAL;
261 }
bc9c80fe
CK
262
263 if (!(flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT))
264 return ret;
265
266 memset(&wait, 0, sizeof(wait));
267 wait.task = current;
268 wait.point = point;
269 drm_syncobj_fence_add_wait(syncobj, &wait);
270
271 do {
272 set_current_state(TASK_INTERRUPTIBLE);
273 if (wait.fence) {
274 ret = 0;
275 break;
276 }
277 if (timeout == 0) {
278 ret = -ETIME;
279 break;
280 }
281
282 if (signal_pending(current)) {
283 ret = -ERESTARTSYS;
284 break;
285 }
286
287 timeout = schedule_timeout(timeout);
288 } while (1);
289
290 __set_current_state(TASK_RUNNING);
291 *fence = wait.fence;
292
293 if (wait.node.next)
294 drm_syncobj_remove_wait(syncobj, &wait);
295
e9083420
DA
296 return ret;
297}
afaf5923 298EXPORT_SYMBOL(drm_syncobj_find_fence);
e9083420
DA
299
300/**
301 * drm_syncobj_free - free a sync object.
302 * @kref: kref to free.
303 *
304 * Only to be called from kref_put in drm_syncobj_put.
305 */
306void drm_syncobj_free(struct kref *kref)
307{
308 struct drm_syncobj *syncobj = container_of(kref,
309 struct drm_syncobj,
310 refcount);
0b258ed1 311 drm_syncobj_replace_fence(syncobj, NULL);
e9083420
DA
312 kfree(syncobj);
313}
314EXPORT_SYMBOL(drm_syncobj_free);
315
1321fd2c
MO
316/**
317 * drm_syncobj_create - create a new syncobj
318 * @out_syncobj: returned syncobj
319 * @flags: DRM_SYNCOBJ_* flags
320 * @fence: if non-NULL, the syncobj will represent this fence
924fe8df
DV
321 *
322 * This is the first function to create a sync object. After creating, drivers
323 * probably want to make it available to userspace, either through
324 * drm_syncobj_get_handle() or drm_syncobj_get_fd().
325 *
326 * Returns 0 on success or a negative error value on failure.
1321fd2c
MO
327 */
328int drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags,
329 struct dma_fence *fence)
e9083420 330{
e9083420
DA
331 struct drm_syncobj *syncobj;
332
333 syncobj = kzalloc(sizeof(struct drm_syncobj), GFP_KERNEL);
334 if (!syncobj)
335 return -ENOMEM;
336
337 kref_init(&syncobj->refcount);
9c19fb10 338 INIT_LIST_HEAD(&syncobj->cb_list);
131280a1 339 spin_lock_init(&syncobj->lock);
e9083420 340
86bbd89d
CK
341 if (flags & DRM_SYNCOBJ_CREATE_SIGNALED)
342 drm_syncobj_assign_null_handle(syncobj);
1fc08218 343
1321fd2c 344 if (fence)
0b258ed1 345 drm_syncobj_replace_fence(syncobj, fence);
1321fd2c
MO
346
347 *out_syncobj = syncobj;
348 return 0;
349}
350EXPORT_SYMBOL(drm_syncobj_create);
351
352/**
353 * drm_syncobj_get_handle - get a handle from a syncobj
924fe8df
DV
354 * @file_private: drm file private pointer
355 * @syncobj: Sync object to export
356 * @handle: out parameter with the new handle
357 *
358 * Exports a sync object created with drm_syncobj_create() as a handle on
359 * @file_private to userspace.
360 *
361 * Returns 0 on success or a negative error value on failure.
1321fd2c
MO
362 */
363int drm_syncobj_get_handle(struct drm_file *file_private,
364 struct drm_syncobj *syncobj, u32 *handle)
365{
366 int ret;
367
368 /* take a reference to put in the idr */
369 drm_syncobj_get(syncobj);
370
e9083420
DA
371 idr_preload(GFP_KERNEL);
372 spin_lock(&file_private->syncobj_table_lock);
373 ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT);
374 spin_unlock(&file_private->syncobj_table_lock);
375
376 idr_preload_end();
377
378 if (ret < 0) {
379 drm_syncobj_put(syncobj);
380 return ret;
381 }
382
383 *handle = ret;
384 return 0;
385}
1321fd2c
MO
386EXPORT_SYMBOL(drm_syncobj_get_handle);
387
388static int drm_syncobj_create_as_handle(struct drm_file *file_private,
389 u32 *handle, uint32_t flags)
390{
391 int ret;
392 struct drm_syncobj *syncobj;
393
394 ret = drm_syncobj_create(&syncobj, flags, NULL);
395 if (ret)
396 return ret;
397
398 ret = drm_syncobj_get_handle(file_private, syncobj, handle);
399 drm_syncobj_put(syncobj);
400 return ret;
401}
e9083420
DA
402
403static int drm_syncobj_destroy(struct drm_file *file_private,
404 u32 handle)
405{
406 struct drm_syncobj *syncobj;
407
408 spin_lock(&file_private->syncobj_table_lock);
409 syncobj = idr_remove(&file_private->syncobj_idr, handle);
410 spin_unlock(&file_private->syncobj_table_lock);
411
412 if (!syncobj)
413 return -EINVAL;
414
415 drm_syncobj_put(syncobj);
416 return 0;
417}
418
419static int drm_syncobj_file_release(struct inode *inode, struct file *file)
420{
421 struct drm_syncobj *syncobj = file->private_data;
422
423 drm_syncobj_put(syncobj);
424 return 0;
425}
426
427static const struct file_operations drm_syncobj_file_fops = {
428 .release = drm_syncobj_file_release,
429};
430
924fe8df
DV
431/**
432 * drm_syncobj_get_fd - get a file descriptor from a syncobj
433 * @syncobj: Sync object to export
434 * @p_fd: out parameter with the new file descriptor
435 *
436 * Exports a sync object created with drm_syncobj_create() as a file descriptor.
437 *
438 * Returns 0 on success or a negative error value on failure.
439 */
684fd0af 440int drm_syncobj_get_fd(struct drm_syncobj *syncobj, int *p_fd)
e9083420 441{
e7cdf5c8 442 struct file *file;
e9083420
DA
443 int fd;
444
e9083420 445 fd = get_unused_fd_flags(O_CLOEXEC);
684fd0af 446 if (fd < 0)
e9083420 447 return fd;
e9083420 448
e7cdf5c8
CW
449 file = anon_inode_getfile("syncobj_file",
450 &drm_syncobj_file_fops,
451 syncobj, 0);
452 if (IS_ERR(file)) {
453 put_unused_fd(fd);
454 return PTR_ERR(file);
e9083420 455 }
e7cdf5c8
CW
456
457 drm_syncobj_get(syncobj);
458 fd_install(fd, file);
459
e9083420
DA
460 *p_fd = fd;
461 return 0;
684fd0af
MO
462}
463EXPORT_SYMBOL(drm_syncobj_get_fd);
464
465static int drm_syncobj_handle_to_fd(struct drm_file *file_private,
466 u32 handle, int *p_fd)
467{
468 struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);
469 int ret;
470
471 if (!syncobj)
472 return -EINVAL;
473
474 ret = drm_syncobj_get_fd(syncobj, p_fd);
e9083420
DA
475 drm_syncobj_put(syncobj);
476 return ret;
477}
478
e9083420
DA
479static int drm_syncobj_fd_to_handle(struct drm_file *file_private,
480 int fd, u32 *handle)
481{
e7cdf5c8 482 struct drm_syncobj *syncobj;
fb386243 483 struct fd f = fdget(fd);
e9083420
DA
484 int ret;
485
fb386243 486 if (!f.file)
e9083420
DA
487 return -EINVAL;
488
fb386243
AV
489 if (f.file->f_op != &drm_syncobj_file_fops) {
490 fdput(f);
e7cdf5c8
CW
491 return -EINVAL;
492 }
493
e9083420 494 /* take a reference to put in the idr */
fb386243 495 syncobj = f.file->private_data;
e9083420
DA
496 drm_syncobj_get(syncobj);
497
498 idr_preload(GFP_KERNEL);
499 spin_lock(&file_private->syncobj_table_lock);
500 ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT);
501 spin_unlock(&file_private->syncobj_table_lock);
502 idr_preload_end();
503
e7cdf5c8
CW
504 if (ret > 0) {
505 *handle = ret;
506 ret = 0;
507 } else
508 drm_syncobj_put(syncobj);
509
fb386243 510 fdput(f);
e7cdf5c8 511 return ret;
e9083420
DA
512}
513
a32c94af
VS
514static int drm_syncobj_import_sync_file_fence(struct drm_file *file_private,
515 int fd, int handle)
3ee45a3b
DA
516{
517 struct dma_fence *fence = sync_file_get_fence(fd);
518 struct drm_syncobj *syncobj;
519
520 if (!fence)
521 return -EINVAL;
522
523 syncobj = drm_syncobj_find(file_private, handle);
524 if (!syncobj) {
525 dma_fence_put(fence);
526 return -ENOENT;
527 }
528
0b258ed1 529 drm_syncobj_replace_fence(syncobj, fence);
3ee45a3b
DA
530 dma_fence_put(fence);
531 drm_syncobj_put(syncobj);
532 return 0;
533}
534
a32c94af
VS
535static int drm_syncobj_export_sync_file(struct drm_file *file_private,
536 int handle, int *p_fd)
3ee45a3b
DA
537{
538 int ret;
539 struct dma_fence *fence;
540 struct sync_file *sync_file;
541 int fd = get_unused_fd_flags(O_CLOEXEC);
542
543 if (fd < 0)
544 return fd;
545
649fdce2 546 ret = drm_syncobj_find_fence(file_private, handle, 0, 0, &fence);
3ee45a3b
DA
547 if (ret)
548 goto err_put_fd;
549
550 sync_file = sync_file_create(fence);
551
552 dma_fence_put(fence);
553
554 if (!sync_file) {
555 ret = -EINVAL;
556 goto err_put_fd;
557 }
558
559 fd_install(fd, sync_file->file);
560
561 *p_fd = fd;
562 return 0;
563err_put_fd:
564 put_unused_fd(fd);
565 return ret;
566}
e9083420
DA
567/**
568 * drm_syncobj_open - initalizes syncobj file-private structures at devnode open time
e9083420
DA
569 * @file_private: drm file-private structure to set up
570 *
571 * Called at device open time, sets up the structure for handling refcounting
572 * of sync objects.
573 */
574void
575drm_syncobj_open(struct drm_file *file_private)
576{
e86584c5 577 idr_init_base(&file_private->syncobj_idr, 1);
e9083420
DA
578 spin_lock_init(&file_private->syncobj_table_lock);
579}
580
581static int
582drm_syncobj_release_handle(int id, void *ptr, void *data)
583{
584 struct drm_syncobj *syncobj = ptr;
585
586 drm_syncobj_put(syncobj);
587 return 0;
588}
589
590/**
591 * drm_syncobj_release - release file-private sync object resources
e9083420
DA
592 * @file_private: drm file-private structure to clean up
593 *
594 * Called at close time when the filp is going away.
595 *
596 * Releases any remaining references on objects by this filp.
597 */
598void
599drm_syncobj_release(struct drm_file *file_private)
600{
601 idr_for_each(&file_private->syncobj_idr,
602 &drm_syncobj_release_handle, file_private);
603 idr_destroy(&file_private->syncobj_idr);
604}
605
606int
607drm_syncobj_create_ioctl(struct drm_device *dev, void *data,
608 struct drm_file *file_private)
609{
610 struct drm_syncobj_create *args = data;
611
612 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
69fdf420 613 return -EOPNOTSUPP;
e9083420
DA
614
615 /* no valid flags yet */
131280a1 616 if (args->flags & ~DRM_SYNCOBJ_CREATE_SIGNALED)
e9083420
DA
617 return -EINVAL;
618
1321fd2c
MO
619 return drm_syncobj_create_as_handle(file_private,
620 &args->handle, args->flags);
e9083420
DA
621}
622
623int
624drm_syncobj_destroy_ioctl(struct drm_device *dev, void *data,
625 struct drm_file *file_private)
626{
627 struct drm_syncobj_destroy *args = data;
628
629 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
69fdf420 630 return -EOPNOTSUPP;
e9083420
DA
631
632 /* make sure padding is empty */
633 if (args->pad)
634 return -EINVAL;
635 return drm_syncobj_destroy(file_private, args->handle);
636}
637
638int
639drm_syncobj_handle_to_fd_ioctl(struct drm_device *dev, void *data,
640 struct drm_file *file_private)
641{
642 struct drm_syncobj_handle *args = data;
643
644 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
69fdf420 645 return -EOPNOTSUPP;
e9083420 646
3ee45a3b 647 if (args->pad)
e9083420
DA
648 return -EINVAL;
649
3ee45a3b
DA
650 if (args->flags != 0 &&
651 args->flags != DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE)
652 return -EINVAL;
653
654 if (args->flags & DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE)
655 return drm_syncobj_export_sync_file(file_private, args->handle,
656 &args->fd);
657
e9083420
DA
658 return drm_syncobj_handle_to_fd(file_private, args->handle,
659 &args->fd);
660}
661
662int
663drm_syncobj_fd_to_handle_ioctl(struct drm_device *dev, void *data,
664 struct drm_file *file_private)
665{
666 struct drm_syncobj_handle *args = data;
667
668 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
69fdf420 669 return -EOPNOTSUPP;
e9083420 670
3ee45a3b
DA
671 if (args->pad)
672 return -EINVAL;
673
674 if (args->flags != 0 &&
675 args->flags != DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE)
e9083420
DA
676 return -EINVAL;
677
3ee45a3b
DA
678 if (args->flags & DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE)
679 return drm_syncobj_import_sync_file_fence(file_private,
680 args->fd,
681 args->handle);
682
e9083420
DA
683 return drm_syncobj_fd_to_handle(file_private, args->fd,
684 &args->handle);
685}
5e60a10e 686
ea569910
CZ
687static int drm_syncobj_transfer_to_timeline(struct drm_file *file_private,
688 struct drm_syncobj_transfer *args)
689{
690 struct drm_syncobj *timeline_syncobj = NULL;
691 struct dma_fence *fence;
692 struct dma_fence_chain *chain;
693 int ret;
694
695 timeline_syncobj = drm_syncobj_find(file_private, args->dst_handle);
696 if (!timeline_syncobj) {
697 return -ENOENT;
698 }
699 ret = drm_syncobj_find_fence(file_private, args->src_handle,
700 args->src_point, args->flags,
701 &fence);
702 if (ret)
703 goto err;
704 chain = kzalloc(sizeof(struct dma_fence_chain), GFP_KERNEL);
705 if (!chain) {
706 ret = -ENOMEM;
707 goto err1;
708 }
709 drm_syncobj_add_point(timeline_syncobj, chain, fence, args->dst_point);
710err1:
711 dma_fence_put(fence);
712err:
713 drm_syncobj_put(timeline_syncobj);
714
715 return ret;
716}
717
718static int
719drm_syncobj_transfer_to_binary(struct drm_file *file_private,
720 struct drm_syncobj_transfer *args)
721{
722 struct drm_syncobj *binary_syncobj = NULL;
723 struct dma_fence *fence;
724 int ret;
725
726 binary_syncobj = drm_syncobj_find(file_private, args->dst_handle);
727 if (!binary_syncobj)
728 return -ENOENT;
729 ret = drm_syncobj_find_fence(file_private, args->src_handle,
730 args->src_point, args->flags, &fence);
731 if (ret)
732 goto err;
733 drm_syncobj_replace_fence(binary_syncobj, fence);
734 dma_fence_put(fence);
735err:
736 drm_syncobj_put(binary_syncobj);
737
738 return ret;
739}
740int
741drm_syncobj_transfer_ioctl(struct drm_device *dev, void *data,
742 struct drm_file *file_private)
743{
744 struct drm_syncobj_transfer *args = data;
745 int ret;
746
060cebb2 747 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
5ec77638 748 return -EOPNOTSUPP;
ea569910
CZ
749
750 if (args->pad)
751 return -EINVAL;
752
753 if (args->dst_point)
754 ret = drm_syncobj_transfer_to_timeline(file_private, args);
755 else
756 ret = drm_syncobj_transfer_to_binary(file_private, args);
757
758 return ret;
759}
760
e7aca503
JE
761static void syncobj_wait_fence_func(struct dma_fence *fence,
762 struct dma_fence_cb *cb)
763{
764 struct syncobj_wait_entry *wait =
765 container_of(cb, struct syncobj_wait_entry, fence_cb);
766
767 wake_up_process(wait->task);
768}
769
770static void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj,
61a98b1b 771 struct syncobj_wait_entry *wait)
e7aca503 772{
01d6c357
CZ
773 struct dma_fence *fence;
774
131280a1 775 /* This happens inside the syncobj lock */
01d6c357
CZ
776 fence = rcu_dereference_protected(syncobj->fence,
777 lockdep_is_held(&syncobj->lock));
778 dma_fence_get(fence);
779 if (!fence || dma_fence_chain_find_seqno(&fence, wait->point)) {
780 dma_fence_put(fence);
781 return;
782 } else if (!fence) {
783 wait->fence = dma_fence_get_stub();
784 } else {
785 wait->fence = fence;
786 }
787
e7aca503 788 wake_up_process(wait->task);
01d6c357 789 list_del_init(&wait->node);
e7aca503
JE
790}
791
792static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs,
01d6c357 793 void __user *user_points,
e7aca503
JE
794 uint32_t count,
795 uint32_t flags,
796 signed long timeout,
797 uint32_t *idx)
798{
799 struct syncobj_wait_entry *entries;
800 struct dma_fence *fence;
01d6c357 801 uint64_t *points;
e7aca503
JE
802 uint32_t signaled_count, i;
803
01d6c357
CZ
804 points = kmalloc_array(count, sizeof(*points), GFP_KERNEL);
805 if (points == NULL)
e7aca503
JE
806 return -ENOMEM;
807
01d6c357
CZ
808 if (!user_points) {
809 memset(points, 0, count * sizeof(uint64_t));
810
811 } else if (copy_from_user(points, user_points,
812 sizeof(uint64_t) * count)) {
813 timeout = -EFAULT;
814 goto err_free_points;
815 }
816
817 entries = kcalloc(count, sizeof(*entries), GFP_KERNEL);
818 if (!entries) {
819 timeout = -ENOMEM;
820 goto err_free_points;
821 }
e7aca503
JE
822 /* Walk the list of sync objects and initialize entries. We do
823 * this up-front so that we can properly return -EINVAL if there is
824 * a syncobj with a missing fence and then never have the chance of
825 * returning -EINVAL again.
826 */
827 signaled_count = 0;
828 for (i = 0; i < count; ++i) {
01d6c357
CZ
829 struct dma_fence *fence;
830
e7aca503 831 entries[i].task = current;
01d6c357
CZ
832 entries[i].point = points[i];
833 fence = drm_syncobj_fence_get(syncobjs[i]);
834 if (!fence || dma_fence_chain_find_seqno(&fence, points[i])) {
835 dma_fence_put(fence);
e7aca503
JE
836 if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) {
837 continue;
838 } else {
12fec62a 839 timeout = -EINVAL;
e7aca503
JE
840 goto cleanup_entries;
841 }
842 }
843
01d6c357
CZ
844 if (fence)
845 entries[i].fence = fence;
846 else
847 entries[i].fence = dma_fence_get_stub();
848
849 if ((flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE) ||
850 dma_fence_is_signaled(entries[i].fence)) {
e7aca503
JE
851 if (signaled_count == 0 && idx)
852 *idx = i;
853 signaled_count++;
854 }
855 }
856
e7aca503
JE
857 if (signaled_count == count ||
858 (signaled_count > 0 &&
859 !(flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL)))
860 goto cleanup_entries;
861
862 /* There's a very annoying laxness in the dma_fence API here, in
863 * that backends are not required to automatically report when a
864 * fence is signaled prior to fence->ops->enable_signaling() being
865 * called. So here if we fail to match signaled_count, we need to
866 * fallthough and try a 0 timeout wait!
867 */
868
869 if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) {
61a98b1b
CK
870 for (i = 0; i < count; ++i)
871 drm_syncobj_fence_add_wait(syncobjs[i], &entries[i]);
e7aca503
JE
872 }
873
874 do {
875 set_current_state(TASK_INTERRUPTIBLE);
876
877 signaled_count = 0;
878 for (i = 0; i < count; ++i) {
879 fence = entries[i].fence;
880 if (!fence)
881 continue;
882
01d6c357
CZ
883 if ((flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE) ||
884 dma_fence_is_signaled(fence) ||
e7aca503
JE
885 (!entries[i].fence_cb.func &&
886 dma_fence_add_callback(fence,
887 &entries[i].fence_cb,
888 syncobj_wait_fence_func))) {
889 /* The fence has been signaled */
890 if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL) {
891 signaled_count++;
892 } else {
893 if (idx)
894 *idx = i;
895 goto done_waiting;
896 }
897 }
898 }
899
900 if (signaled_count == count)
901 goto done_waiting;
902
903 if (timeout == 0) {
12fec62a 904 timeout = -ETIME;
e7aca503
JE
905 goto done_waiting;
906 }
907
12fec62a
CW
908 if (signal_pending(current)) {
909 timeout = -ERESTARTSYS;
910 goto done_waiting;
911 }
e7aca503 912
12fec62a
CW
913 timeout = schedule_timeout(timeout);
914 } while (1);
e7aca503
JE
915
916done_waiting:
917 __set_current_state(TASK_RUNNING);
918
919cleanup_entries:
920 for (i = 0; i < count; ++i) {
61a98b1b 921 drm_syncobj_remove_wait(syncobjs[i], &entries[i]);
e7aca503
JE
922 if (entries[i].fence_cb.func)
923 dma_fence_remove_callback(entries[i].fence,
924 &entries[i].fence_cb);
925 dma_fence_put(entries[i].fence);
926 }
927 kfree(entries);
928
01d6c357
CZ
929err_free_points:
930 kfree(points);
931
12fec62a 932 return timeout;
e7aca503
JE
933}
934
5e60a10e
DA
935/**
936 * drm_timeout_abs_to_jiffies - calculate jiffies timeout from absolute value
937 *
938 * @timeout_nsec: timeout nsec component in ns, 0 for poll
939 *
940 * Calculate the timeout in jiffies from an absolute time in sec/nsec.
941 */
877b3729 942signed long drm_timeout_abs_to_jiffies(int64_t timeout_nsec)
5e60a10e
DA
943{
944 ktime_t abs_timeout, now;
945 u64 timeout_ns, timeout_jiffies64;
946
947 /* make 0 timeout means poll - absolute 0 doesn't seem valid */
948 if (timeout_nsec == 0)
949 return 0;
950
951 abs_timeout = ns_to_ktime(timeout_nsec);
952 now = ktime_get();
953
954 if (!ktime_after(abs_timeout, now))
955 return 0;
956
957 timeout_ns = ktime_to_ns(ktime_sub(abs_timeout, now));
958
959 timeout_jiffies64 = nsecs_to_jiffies64(timeout_ns);
960 /* clamp timeout to avoid infinite timeout */
961 if (timeout_jiffies64 >= MAX_SCHEDULE_TIMEOUT - 1)
962 return MAX_SCHEDULE_TIMEOUT - 1;
963
964 return timeout_jiffies64 + 1;
965}
877b3729 966EXPORT_SYMBOL(drm_timeout_abs_to_jiffies);
5e60a10e 967
e7aca503
JE
968static int drm_syncobj_array_wait(struct drm_device *dev,
969 struct drm_file *file_private,
970 struct drm_syncobj_wait *wait,
01d6c357
CZ
971 struct drm_syncobj_timeline_wait *timeline_wait,
972 struct drm_syncobj **syncobjs, bool timeline)
5e60a10e 973{
01d6c357 974 signed long timeout = 0;
5e60a10e
DA
975 uint32_t first = ~0;
976
01d6c357
CZ
977 if (!timeline) {
978 timeout = drm_timeout_abs_to_jiffies(wait->timeout_nsec);
979 timeout = drm_syncobj_array_wait_timeout(syncobjs,
980 NULL,
981 wait->count_handles,
982 wait->flags,
983 timeout, &first);
984 if (timeout < 0)
985 return timeout;
986 wait->first_signaled = first;
987 } else {
988 timeout = drm_timeout_abs_to_jiffies(timeline_wait->timeout_nsec);
989 timeout = drm_syncobj_array_wait_timeout(syncobjs,
990 u64_to_user_ptr(timeline_wait->points),
991 timeline_wait->count_handles,
992 timeline_wait->flags,
993 timeout, &first);
994 if (timeout < 0)
995 return timeout;
996 timeline_wait->first_signaled = first;
997 }
5e60a10e
DA
998 return 0;
999}
1000
3e6fb72d 1001static int drm_syncobj_array_find(struct drm_file *file_private,
9e554462
VS
1002 void __user *user_handles,
1003 uint32_t count_handles,
3e6fb72d 1004 struct drm_syncobj ***syncobjs_out)
5e60a10e 1005{
3e6fb72d 1006 uint32_t i, *handles;
e7aca503 1007 struct drm_syncobj **syncobjs;
3e6fb72d 1008 int ret;
5e60a10e 1009
3e6fb72d 1010 handles = kmalloc_array(count_handles, sizeof(*handles), GFP_KERNEL);
5e60a10e
DA
1011 if (handles == NULL)
1012 return -ENOMEM;
1013
3e6fb72d
JE
1014 if (copy_from_user(handles, user_handles,
1015 sizeof(uint32_t) * count_handles)) {
5e60a10e
DA
1016 ret = -EFAULT;
1017 goto err_free_handles;
1018 }
1019
3e6fb72d
JE
1020 syncobjs = kmalloc_array(count_handles, sizeof(*syncobjs), GFP_KERNEL);
1021 if (syncobjs == NULL) {
5e60a10e
DA
1022 ret = -ENOMEM;
1023 goto err_free_handles;
1024 }
1025
3e6fb72d 1026 for (i = 0; i < count_handles; i++) {
e7aca503
JE
1027 syncobjs[i] = drm_syncobj_find(file_private, handles[i]);
1028 if (!syncobjs[i]) {
1029 ret = -ENOENT;
3e6fb72d 1030 goto err_put_syncobjs;
e7aca503 1031 }
5e60a10e
DA
1032 }
1033
3e6fb72d
JE
1034 kfree(handles);
1035 *syncobjs_out = syncobjs;
1036 return 0;
5e60a10e 1037
3e6fb72d 1038err_put_syncobjs:
e7aca503
JE
1039 while (i-- > 0)
1040 drm_syncobj_put(syncobjs[i]);
1041 kfree(syncobjs);
5e60a10e
DA
1042err_free_handles:
1043 kfree(handles);
1044
1045 return ret;
1046}
3e6fb72d
JE
1047
1048static void drm_syncobj_array_free(struct drm_syncobj **syncobjs,
1049 uint32_t count)
1050{
1051 uint32_t i;
1052 for (i = 0; i < count; i++)
1053 drm_syncobj_put(syncobjs[i]);
1054 kfree(syncobjs);
1055}
1056
1057int
1058drm_syncobj_wait_ioctl(struct drm_device *dev, void *data,
1059 struct drm_file *file_private)
1060{
1061 struct drm_syncobj_wait *args = data;
1062 struct drm_syncobj **syncobjs;
1063 int ret = 0;
1064
1065 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
69fdf420 1066 return -EOPNOTSUPP;
3e6fb72d
JE
1067
1068 if (args->flags & ~(DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL |
1069 DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT))
1070 return -EINVAL;
1071
1072 if (args->count_handles == 0)
1073 return -EINVAL;
1074
1075 ret = drm_syncobj_array_find(file_private,
1076 u64_to_user_ptr(args->handles),
1077 args->count_handles,
1078 &syncobjs);
1079 if (ret < 0)
1080 return ret;
1081
1082 ret = drm_syncobj_array_wait(dev, file_private,
01d6c357 1083 args, NULL, syncobjs, false);
3e6fb72d
JE
1084
1085 drm_syncobj_array_free(syncobjs, args->count_handles);
1086
1087 return ret;
1088}
aa4035d2 1089
01d6c357
CZ
1090int
1091drm_syncobj_timeline_wait_ioctl(struct drm_device *dev, void *data,
1092 struct drm_file *file_private)
1093{
1094 struct drm_syncobj_timeline_wait *args = data;
1095 struct drm_syncobj **syncobjs;
1096 int ret = 0;
1097
060cebb2 1098 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
5ec77638 1099 return -EOPNOTSUPP;
01d6c357
CZ
1100
1101 if (args->flags & ~(DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL |
1102 DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
1103 DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE))
1104 return -EINVAL;
1105
1106 if (args->count_handles == 0)
1107 return -EINVAL;
1108
1109 ret = drm_syncobj_array_find(file_private,
1110 u64_to_user_ptr(args->handles),
1111 args->count_handles,
1112 &syncobjs);
1113 if (ret < 0)
1114 return ret;
1115
1116 ret = drm_syncobj_array_wait(dev, file_private,
1117 NULL, args, syncobjs, true);
3e6fb72d
JE
1118
1119 drm_syncobj_array_free(syncobjs, args->count_handles);
1120
1121 return ret;
1122}
aa4035d2 1123
01d6c357 1124
aa4035d2
JE
1125int
1126drm_syncobj_reset_ioctl(struct drm_device *dev, void *data,
1127 struct drm_file *file_private)
1128{
1129 struct drm_syncobj_array *args = data;
1130 struct drm_syncobj **syncobjs;
1131 uint32_t i;
1132 int ret;
1133
1134 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
69fdf420 1135 return -EOPNOTSUPP;
aa4035d2
JE
1136
1137 if (args->pad != 0)
1138 return -EINVAL;
1139
1140 if (args->count_handles == 0)
1141 return -EINVAL;
1142
1143 ret = drm_syncobj_array_find(file_private,
1144 u64_to_user_ptr(args->handles),
1145 args->count_handles,
1146 &syncobjs);
1147 if (ret < 0)
1148 return ret;
1149
131280a1 1150 for (i = 0; i < args->count_handles; i++)
0b258ed1 1151 drm_syncobj_replace_fence(syncobjs[i], NULL);
131280a1 1152
aa4035d2
JE
1153 drm_syncobj_array_free(syncobjs, args->count_handles);
1154
131280a1 1155 return 0;
aa4035d2 1156}
ffa9443f
JE
1157
1158int
1159drm_syncobj_signal_ioctl(struct drm_device *dev, void *data,
1160 struct drm_file *file_private)
1161{
1162 struct drm_syncobj_array *args = data;
1163 struct drm_syncobj **syncobjs;
1164 uint32_t i;
1165 int ret;
1166
1167 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
69fdf420 1168 return -EOPNOTSUPP;
ffa9443f
JE
1169
1170 if (args->pad != 0)
1171 return -EINVAL;
1172
1173 if (args->count_handles == 0)
1174 return -EINVAL;
1175
1176 ret = drm_syncobj_array_find(file_private,
1177 u64_to_user_ptr(args->handles),
1178 args->count_handles,
1179 &syncobjs);
1180 if (ret < 0)
1181 return ret;
1182
86bbd89d
CK
1183 for (i = 0; i < args->count_handles; i++)
1184 drm_syncobj_assign_null_handle(syncobjs[i]);
ffa9443f
JE
1185
1186 drm_syncobj_array_free(syncobjs, args->count_handles);
1187
1188 return ret;
1189}
27b575a9 1190
50d1ebef
CZ
1191int
1192drm_syncobj_timeline_signal_ioctl(struct drm_device *dev, void *data,
1193 struct drm_file *file_private)
1194{
1195 struct drm_syncobj_timeline_array *args = data;
1196 struct drm_syncobj **syncobjs;
1197 struct dma_fence_chain **chains;
1198 uint64_t *points;
1199 uint32_t i, j;
1200 int ret;
1201
060cebb2 1202 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
50d1ebef
CZ
1203 return -EOPNOTSUPP;
1204
1205 if (args->pad != 0)
1206 return -EINVAL;
1207
1208 if (args->count_handles == 0)
1209 return -EINVAL;
1210
1211 ret = drm_syncobj_array_find(file_private,
1212 u64_to_user_ptr(args->handles),
1213 args->count_handles,
1214 &syncobjs);
1215 if (ret < 0)
1216 return ret;
1217
1218 points = kmalloc_array(args->count_handles, sizeof(*points),
1219 GFP_KERNEL);
1220 if (!points) {
1221 ret = -ENOMEM;
1222 goto out;
1223 }
1224 if (!u64_to_user_ptr(args->points)) {
1225 memset(points, 0, args->count_handles * sizeof(uint64_t));
1226 } else if (copy_from_user(points, u64_to_user_ptr(args->points),
1227 sizeof(uint64_t) * args->count_handles)) {
1228 ret = -EFAULT;
1229 goto err_points;
1230 }
1231
1232 chains = kmalloc_array(args->count_handles, sizeof(void *), GFP_KERNEL);
1233 if (!chains) {
1234 ret = -ENOMEM;
1235 goto err_points;
1236 }
1237 for (i = 0; i < args->count_handles; i++) {
1238 chains[i] = kzalloc(sizeof(struct dma_fence_chain), GFP_KERNEL);
1239 if (!chains[i]) {
1240 for (j = 0; j < i; j++)
1241 kfree(chains[j]);
1242 ret = -ENOMEM;
1243 goto err_chains;
1244 }
1245 }
1246
1247 for (i = 0; i < args->count_handles; i++) {
1248 struct dma_fence *fence = dma_fence_get_stub();
1249
1250 drm_syncobj_add_point(syncobjs[i], chains[i],
1251 fence, points[i]);
1252 dma_fence_put(fence);
1253 }
1254err_chains:
1255 kfree(chains);
1256err_points:
1257 kfree(points);
1258out:
1259 drm_syncobj_array_free(syncobjs, args->count_handles);
1260
1261 return ret;
1262}
1263
27b575a9
CZ
1264int drm_syncobj_query_ioctl(struct drm_device *dev, void *data,
1265 struct drm_file *file_private)
1266{
1267 struct drm_syncobj_timeline_array *args = data;
1268 struct drm_syncobj **syncobjs;
1269 uint64_t __user *points = u64_to_user_ptr(args->points);
1270 uint32_t i;
1271 int ret;
1272
060cebb2
LL
1273 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
1274 return -EOPNOTSUPP;
27b575a9
CZ
1275
1276 if (args->pad != 0)
1277 return -EINVAL;
1278
1279 if (args->count_handles == 0)
1280 return -EINVAL;
1281
1282 ret = drm_syncobj_array_find(file_private,
1283 u64_to_user_ptr(args->handles),
1284 args->count_handles,
1285 &syncobjs);
1286 if (ret < 0)
1287 return ret;
1288
1289 for (i = 0; i < args->count_handles; i++) {
1290 struct dma_fence_chain *chain;
1291 struct dma_fence *fence;
1292 uint64_t point;
1293
1294 fence = drm_syncobj_fence_get(syncobjs[i]);
1295 chain = to_dma_fence_chain(fence);
1296 if (chain) {
1297 struct dma_fence *iter, *last_signaled = NULL;
1298
1299 dma_fence_chain_for_each(iter, fence) {
1300 if (!iter)
1301 break;
1302 dma_fence_put(last_signaled);
1303 last_signaled = dma_fence_get(iter);
1304 if (!to_dma_fence_chain(last_signaled)->prev_seqno)
1305 /* It is most likely that timeline has
1306 * unorder points. */
1307 break;
1308 }
1309 point = dma_fence_is_signaled(last_signaled) ?
1310 last_signaled->seqno :
1311 to_dma_fence_chain(last_signaled)->prev_seqno;
1312 dma_fence_put(last_signaled);
1313 } else {
1314 point = 0;
1315 }
1316 ret = copy_to_user(&points[i], &point, sizeof(uint64_t));
1317 ret = ret ? -EFAULT : 0;
1318 if (ret)
1319 break;
1320 }
1321 drm_syncobj_array_free(syncobjs, args->count_handles);
1322
1323 return ret;
1324}