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