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