xref: /linux-6.15/drivers/gpu/drm/drm_syncobj.c (revision 6348be02)
1e9083420SDave Airlie /*
2e9083420SDave Airlie  * Copyright 2017 Red Hat
35e60a10eSDave Airlie  * Parts ported from amdgpu (fence wait code).
45e60a10eSDave Airlie  * Copyright 2016 Advanced Micro Devices, Inc.
5e9083420SDave Airlie  *
6e9083420SDave Airlie  * Permission is hereby granted, free of charge, to any person obtaining a
7e9083420SDave Airlie  * copy of this software and associated documentation files (the "Software"),
8e9083420SDave Airlie  * to deal in the Software without restriction, including without limitation
9e9083420SDave Airlie  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10e9083420SDave Airlie  * and/or sell copies of the Software, and to permit persons to whom the
11e9083420SDave Airlie  * Software is furnished to do so, subject to the following conditions:
12e9083420SDave Airlie  *
13e9083420SDave Airlie  * The above copyright notice and this permission notice (including the next
14e9083420SDave Airlie  * paragraph) shall be included in all copies or substantial portions of the
15e9083420SDave Airlie  * Software.
16e9083420SDave Airlie  *
17e9083420SDave Airlie  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18e9083420SDave Airlie  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19e9083420SDave Airlie  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20e9083420SDave Airlie  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21e9083420SDave Airlie  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22e9083420SDave Airlie  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23e9083420SDave Airlie  * IN THE SOFTWARE.
24e9083420SDave Airlie  *
25e9083420SDave Airlie  * Authors:
26e9083420SDave Airlie  *
27e9083420SDave Airlie  */
28e9083420SDave Airlie 
29e9083420SDave Airlie /**
30e9083420SDave Airlie  * DOC: Overview
31e9083420SDave Airlie  *
32f246ff5cSJason Ekstrand  * DRM synchronisation objects (syncobj, see struct &drm_syncobj) provide a
33f246ff5cSJason Ekstrand  * container for a synchronization primitive which can be used by userspace
34f246ff5cSJason Ekstrand  * to explicitly synchronize GPU commands, can be shared between userspace
35f246ff5cSJason Ekstrand  * processes, and can be shared between different DRM drivers.
36e9083420SDave Airlie  * Their primary use-case is to implement Vulkan fences and semaphores.
37f246ff5cSJason Ekstrand  * The syncobj userspace API provides ioctls for several operations:
38e9083420SDave Airlie  *
39f246ff5cSJason Ekstrand  *  - Creation and destruction of syncobjs
40f246ff5cSJason Ekstrand  *  - Import and export of syncobjs to/from a syncobj file descriptor
41f246ff5cSJason Ekstrand  *  - Import and export a syncobj's underlying fence to/from a sync file
42f246ff5cSJason Ekstrand  *  - Reset a syncobj (set its fence to NULL)
43f246ff5cSJason Ekstrand  *  - Signal a syncobj (set a trivially signaled fence)
44f246ff5cSJason Ekstrand  *  - Wait for a syncobj's fence to appear and be signaled
45f246ff5cSJason Ekstrand  *
4677d1a6dbSLionel Landwerlin  * The syncobj userspace API also provides operations to manipulate a syncobj
4777d1a6dbSLionel Landwerlin  * in terms of a timeline of struct &dma_fence_chain rather than a single
4877d1a6dbSLionel Landwerlin  * struct &dma_fence, through the following operations:
4977d1a6dbSLionel Landwerlin  *
5077d1a6dbSLionel Landwerlin  *   - Signal a given point on the timeline
5177d1a6dbSLionel Landwerlin  *   - Wait for a given point to appear and/or be signaled
5277d1a6dbSLionel Landwerlin  *   - Import and export from/to a given point of a timeline
5377d1a6dbSLionel Landwerlin  *
54f246ff5cSJason Ekstrand  * At it's core, a syncobj is simply a wrapper around a pointer to a struct
55f246ff5cSJason Ekstrand  * &dma_fence which may be NULL.
56f246ff5cSJason Ekstrand  * When a syncobj is first created, its pointer is either NULL or a pointer
57f246ff5cSJason Ekstrand  * to an already signaled fence depending on whether the
58f246ff5cSJason Ekstrand  * &DRM_SYNCOBJ_CREATE_SIGNALED flag is passed to
59f246ff5cSJason Ekstrand  * &DRM_IOCTL_SYNCOBJ_CREATE.
6077d1a6dbSLionel Landwerlin  *
6177d1a6dbSLionel Landwerlin  * If the syncobj is considered as a binary (its state is either signaled or
6277d1a6dbSLionel Landwerlin  * unsignaled) primitive, when GPU work is enqueued in a DRM driver to signal
6377d1a6dbSLionel Landwerlin  * the syncobj, the syncobj's fence is replaced with a fence which will be
6477d1a6dbSLionel Landwerlin  * signaled by the completion of that work.
6577d1a6dbSLionel Landwerlin  * If the syncobj is considered as a timeline primitive, when GPU work is
6677d1a6dbSLionel Landwerlin  * enqueued in a DRM driver to signal the a given point of the syncobj, a new
6777d1a6dbSLionel Landwerlin  * struct &dma_fence_chain pointing to the DRM driver's fence and also
6877d1a6dbSLionel Landwerlin  * pointing to the previous fence that was in the syncobj. The new struct
6977d1a6dbSLionel Landwerlin  * &dma_fence_chain fence replace the syncobj's fence and will be signaled by
7077d1a6dbSLionel Landwerlin  * completion of the DRM driver's work and also any work associated with the
7177d1a6dbSLionel Landwerlin  * fence previously in the syncobj.
7277d1a6dbSLionel Landwerlin  *
7377d1a6dbSLionel Landwerlin  * When GPU work which waits on a syncobj is enqueued in a DRM driver, at the
7477d1a6dbSLionel Landwerlin  * time the work is enqueued, it waits on the syncobj's fence before
7577d1a6dbSLionel Landwerlin  * submitting the work to hardware. That fence is either :
7677d1a6dbSLionel Landwerlin  *
7777d1a6dbSLionel Landwerlin  *    - The syncobj's current fence if the syncobj is considered as a binary
7877d1a6dbSLionel Landwerlin  *      primitive.
7977d1a6dbSLionel Landwerlin  *    - The struct &dma_fence associated with a given point if the syncobj is
8077d1a6dbSLionel Landwerlin  *      considered as a timeline primitive.
8177d1a6dbSLionel Landwerlin  *
8277d1a6dbSLionel Landwerlin  * If the syncobj's fence is NULL or not present in the syncobj's timeline,
8377d1a6dbSLionel Landwerlin  * the enqueue operation is expected to fail.
8477d1a6dbSLionel Landwerlin  *
8577d1a6dbSLionel Landwerlin  * With binary syncobj, all manipulation of the syncobjs's fence happens in
8677d1a6dbSLionel Landwerlin  * terms of the current fence at the time the ioctl is called by userspace
8777d1a6dbSLionel Landwerlin  * regardless of whether that operation is an immediate host-side operation
8877d1a6dbSLionel Landwerlin  * (signal or reset) or or an operation which is enqueued in some driver
8977d1a6dbSLionel Landwerlin  * queue. &DRM_IOCTL_SYNCOBJ_RESET and &DRM_IOCTL_SYNCOBJ_SIGNAL can be used
9077d1a6dbSLionel Landwerlin  * to manipulate a syncobj from the host by resetting its pointer to NULL or
91f246ff5cSJason Ekstrand  * setting its pointer to a fence which is already signaled.
92f246ff5cSJason Ekstrand  *
9377d1a6dbSLionel Landwerlin  * With a timeline syncobj, all manipulation of the synobj's fence happens in
9477d1a6dbSLionel Landwerlin  * terms of a u64 value referring to point in the timeline. See
9577d1a6dbSLionel Landwerlin  * dma_fence_chain_find_seqno() to see how a given point is found in the
9677d1a6dbSLionel Landwerlin  * timeline.
9777d1a6dbSLionel Landwerlin  *
9877d1a6dbSLionel Landwerlin  * Note that applications should be careful to always use timeline set of
9977d1a6dbSLionel Landwerlin  * ioctl() when dealing with syncobj considered as timeline. Using a binary
10077d1a6dbSLionel Landwerlin  * set of ioctl() with a syncobj considered as timeline could result incorrect
10177d1a6dbSLionel Landwerlin  * synchronization. The use of binary syncobj is supported through the
10277d1a6dbSLionel Landwerlin  * timeline set of ioctl() by using a point value of 0, this will reproduce
10377d1a6dbSLionel Landwerlin  * the behavior of the binary set of ioctl() (for example replace the
10477d1a6dbSLionel Landwerlin  * syncobj's fence when signaling).
10577d1a6dbSLionel Landwerlin  *
106f246ff5cSJason Ekstrand  *
107f246ff5cSJason Ekstrand  * Host-side wait on syncobjs
108f246ff5cSJason Ekstrand  * --------------------------
109f246ff5cSJason Ekstrand  *
110f246ff5cSJason Ekstrand  * &DRM_IOCTL_SYNCOBJ_WAIT takes an array of syncobj handles and does a
111f246ff5cSJason Ekstrand  * host-side wait on all of the syncobj fences simultaneously.
112f246ff5cSJason Ekstrand  * If &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL is set, the wait ioctl will wait on
113f246ff5cSJason Ekstrand  * all of the syncobj fences to be signaled before it returns.
114f246ff5cSJason Ekstrand  * Otherwise, it returns once at least one syncobj fence has been signaled
115f246ff5cSJason Ekstrand  * and the index of a signaled fence is written back to the client.
116f246ff5cSJason Ekstrand  *
117f246ff5cSJason Ekstrand  * Unlike the enqueued GPU work dependencies which fail if they see a NULL
118f246ff5cSJason Ekstrand  * fence in a syncobj, if &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT is set,
119f246ff5cSJason Ekstrand  * the host-side wait will first wait for the syncobj to receive a non-NULL
120f246ff5cSJason Ekstrand  * fence and then wait on that fence.
121f246ff5cSJason Ekstrand  * If &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT is not set and any one of the
122f246ff5cSJason Ekstrand  * syncobjs in the array has a NULL fence, -EINVAL will be returned.
123f246ff5cSJason Ekstrand  * Assuming the syncobj starts off with a NULL fence, this allows a client
124f246ff5cSJason Ekstrand  * to do a host wait in one thread (or process) which waits on GPU work
125f246ff5cSJason Ekstrand  * submitted in another thread (or process) without having to manually
126f246ff5cSJason Ekstrand  * synchronize between the two.
127f246ff5cSJason Ekstrand  * This requirement is inherited from the Vulkan fence API.
128f246ff5cSJason Ekstrand  *
1298570c279SRob Clark  * If &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_DEADLINE is set, the ioctl will also set
1308570c279SRob Clark  * a fence deadline hint on the backing fences before waiting, to provide the
1318570c279SRob Clark  * fence signaler with an appropriate sense of urgency.  The deadline is
1328570c279SRob Clark  * specified as an absolute &CLOCK_MONOTONIC value in units of ns.
1338570c279SRob Clark  *
13477d1a6dbSLionel Landwerlin  * Similarly, &DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT takes an array of syncobj
13577d1a6dbSLionel Landwerlin  * handles as well as an array of u64 points and does a host-side wait on all
13677d1a6dbSLionel Landwerlin  * of syncobj fences at the given points simultaneously.
13777d1a6dbSLionel Landwerlin  *
13877d1a6dbSLionel Landwerlin  * &DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT also adds the ability to wait for a given
13977d1a6dbSLionel Landwerlin  * fence to materialize on the timeline without waiting for the fence to be
14077d1a6dbSLionel Landwerlin  * signaled by using the &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE flag. This
14177d1a6dbSLionel Landwerlin  * requirement is inherited from the wait-before-signal behavior required by
14277d1a6dbSLionel Landwerlin  * the Vulkan timeline semaphore API.
14377d1a6dbSLionel Landwerlin  *
144c7a47229SSimon Ser  * Alternatively, &DRM_IOCTL_SYNCOBJ_EVENTFD can be used to wait without
145c7a47229SSimon Ser  * blocking: an eventfd will be signaled when the syncobj is. This is useful to
146c7a47229SSimon Ser  * integrate the wait in an event loop.
147c7a47229SSimon Ser  *
148f246ff5cSJason Ekstrand  *
149f246ff5cSJason Ekstrand  * Import/export of syncobjs
150f246ff5cSJason Ekstrand  * -------------------------
151f246ff5cSJason Ekstrand  *
152f246ff5cSJason Ekstrand  * &DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE and &DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD
153f246ff5cSJason Ekstrand  * provide two mechanisms for import/export of syncobjs.
154f246ff5cSJason Ekstrand  *
155f246ff5cSJason Ekstrand  * The first lets the client import or export an entire syncobj to a file
156f246ff5cSJason Ekstrand  * descriptor.
157f246ff5cSJason Ekstrand  * These fd's are opaque and have no other use case, except passing the
158f246ff5cSJason Ekstrand  * syncobj between processes.
159f246ff5cSJason Ekstrand  * All exported file descriptors and any syncobj handles created as a
160f246ff5cSJason Ekstrand  * result of importing those file descriptors own a reference to the
161f246ff5cSJason Ekstrand  * same underlying struct &drm_syncobj and the syncobj can be used
162f246ff5cSJason Ekstrand  * persistently across all the processes with which it is shared.
163f246ff5cSJason Ekstrand  * The syncobj is freed only once the last reference is dropped.
164f246ff5cSJason Ekstrand  * Unlike dma-buf, importing a syncobj creates a new handle (with its own
165f246ff5cSJason Ekstrand  * reference) for every import instead of de-duplicating.
166f246ff5cSJason Ekstrand  * The primary use-case of this persistent import/export is for shared
167f246ff5cSJason Ekstrand  * Vulkan fences and semaphores.
168f246ff5cSJason Ekstrand  *
169f246ff5cSJason Ekstrand  * The second import/export mechanism, which is indicated by
170f246ff5cSJason Ekstrand  * &DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE or
171f246ff5cSJason Ekstrand  * &DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE lets the client
172f246ff5cSJason Ekstrand  * import/export the syncobj's current fence from/to a &sync_file.
173f246ff5cSJason Ekstrand  * When a syncobj is exported to a sync file, that sync file wraps the
174f246ff5cSJason Ekstrand  * sycnobj's fence at the time of export and any later signal or reset
175f246ff5cSJason Ekstrand  * operations on the syncobj will not affect the exported sync file.
176f246ff5cSJason Ekstrand  * When a sync file is imported into a syncobj, the syncobj's fence is set
177f246ff5cSJason Ekstrand  * to the fence wrapped by that sync file.
178f246ff5cSJason Ekstrand  * Because sync files are immutable, resetting or signaling the syncobj
179f246ff5cSJason Ekstrand  * will not affect any sync files whose fences have been imported into the
180f246ff5cSJason Ekstrand  * syncobj.
18177d1a6dbSLionel Landwerlin  *
18277d1a6dbSLionel Landwerlin  *
18377d1a6dbSLionel Landwerlin  * Import/export of timeline points in timeline syncobjs
18477d1a6dbSLionel Landwerlin  * -----------------------------------------------------
18577d1a6dbSLionel Landwerlin  *
18677d1a6dbSLionel Landwerlin  * &DRM_IOCTL_SYNCOBJ_TRANSFER provides a mechanism to transfer a struct
18777d1a6dbSLionel Landwerlin  * &dma_fence_chain of a syncobj at a given u64 point to another u64 point
18877d1a6dbSLionel Landwerlin  * into another syncobj.
18977d1a6dbSLionel Landwerlin  *
19077d1a6dbSLionel Landwerlin  * Note that if you want to transfer a struct &dma_fence_chain from a given
19177d1a6dbSLionel Landwerlin  * point on a timeline syncobj from/into a binary syncobj, you can use the
19277d1a6dbSLionel Landwerlin  * point 0 to mean take/replace the fence in the syncobj.
193e9083420SDave Airlie  */
194e9083420SDave Airlie 
1950500c04eSSam Ravnborg #include <linux/anon_inodes.h>
196ec8d985fSChristian König #include <linux/dma-fence-unwrap.h>
197c7a47229SSimon Ser #include <linux/eventfd.h>
198e9083420SDave Airlie #include <linux/file.h>
199e9083420SDave Airlie #include <linux/fs.h>
200e7aca503SJason Ekstrand #include <linux/sched/signal.h>
2010500c04eSSam Ravnborg #include <linux/sync_file.h>
2020500c04eSSam Ravnborg #include <linux/uaccess.h>
2030500c04eSSam Ravnborg 
204d89281c5SSam Ravnborg #include <drm/drm.h>
2050500c04eSSam Ravnborg #include <drm/drm_drv.h>
2060500c04eSSam Ravnborg #include <drm/drm_file.h>
2070500c04eSSam Ravnborg #include <drm/drm_gem.h>
2080500c04eSSam Ravnborg #include <drm/drm_print.h>
2090500c04eSSam Ravnborg #include <drm/drm_syncobj.h>
210b9436986SVille Syrjälä #include <drm/drm_utils.h>
211e9083420SDave Airlie 
212e9083420SDave Airlie #include "drm_internal.h"
213e9083420SDave Airlie 
21461a98b1bSChristian König struct syncobj_wait_entry {
21561a98b1bSChristian König 	struct list_head node;
21661a98b1bSChristian König 	struct task_struct *task;
21761a98b1bSChristian König 	struct dma_fence *fence;
21861a98b1bSChristian König 	struct dma_fence_cb fence_cb;
21901d6c357SChunming Zhou 	u64    point;
22061a98b1bSChristian König };
22161a98b1bSChristian König 
22261a98b1bSChristian König static void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj,
22361a98b1bSChristian König 				      struct syncobj_wait_entry *wait);
22461a98b1bSChristian König 
225c7a47229SSimon Ser struct syncobj_eventfd_entry {
226c7a47229SSimon Ser 	struct list_head node;
227c7a47229SSimon Ser 	struct dma_fence *fence;
228c7a47229SSimon Ser 	struct dma_fence_cb fence_cb;
229c7a47229SSimon Ser 	struct drm_syncobj *syncobj;
230c7a47229SSimon Ser 	struct eventfd_ctx *ev_fd_ctx;
231c7a47229SSimon Ser 	u64 point;
232c7a47229SSimon Ser 	u32 flags;
233c7a47229SSimon Ser };
234c7a47229SSimon Ser 
235c7a47229SSimon Ser static void
236c7a47229SSimon Ser syncobj_eventfd_entry_func(struct drm_syncobj *syncobj,
237c7a47229SSimon Ser 			   struct syncobj_eventfd_entry *entry);
238c7a47229SSimon Ser 
239e9083420SDave Airlie /**
240e9083420SDave Airlie  * drm_syncobj_find - lookup and reference a sync object.
241e9083420SDave Airlie  * @file_private: drm file private pointer
242e9083420SDave Airlie  * @handle: sync object handle to lookup.
243e9083420SDave Airlie  *
244924fe8dfSDaniel Vetter  * Returns a reference to the syncobj pointed to by handle or NULL. The
245924fe8dfSDaniel Vetter  * reference must be released by calling drm_syncobj_put().
246e9083420SDave Airlie  */
drm_syncobj_find(struct drm_file * file_private,u32 handle)247e9083420SDave Airlie struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private,
248e9083420SDave Airlie 				     u32 handle)
249e9083420SDave Airlie {
250e9083420SDave Airlie 	struct drm_syncobj *syncobj;
251e9083420SDave Airlie 
252e9083420SDave Airlie 	spin_lock(&file_private->syncobj_table_lock);
253e9083420SDave Airlie 
254e9083420SDave Airlie 	/* Check if we currently have a reference on the object */
255e9083420SDave Airlie 	syncobj = idr_find(&file_private->syncobj_idr, handle);
256e9083420SDave Airlie 	if (syncobj)
257e9083420SDave Airlie 		drm_syncobj_get(syncobj);
258e9083420SDave Airlie 
259e9083420SDave Airlie 	spin_unlock(&file_private->syncobj_table_lock);
260e9083420SDave Airlie 
261e9083420SDave Airlie 	return syncobj;
262e9083420SDave Airlie }
263e9083420SDave Airlie EXPORT_SYMBOL(drm_syncobj_find);
264e9083420SDave Airlie 
drm_syncobj_fence_add_wait(struct drm_syncobj * syncobj,struct syncobj_wait_entry * wait)26561a98b1bSChristian König static void drm_syncobj_fence_add_wait(struct drm_syncobj *syncobj,
26661a98b1bSChristian König 				       struct syncobj_wait_entry *wait)
26743cf1fc0SChunming Zhou {
26801d6c357SChunming Zhou 	struct dma_fence *fence;
26901d6c357SChunming Zhou 
27061a98b1bSChristian König 	if (wait->fence)
27161a98b1bSChristian König 		return;
272337fe9f5SJason Ekstrand 
273131280a1SEric Anholt 	spin_lock(&syncobj->lock);
274131280a1SEric Anholt 	/* We've already tried once to get a fence and failed.  Now that we
275131280a1SEric Anholt 	 * have the lock, try one more time just to be sure we don't add a
276131280a1SEric Anholt 	 * callback when a fence has already been set.
277131280a1SEric Anholt 	 */
27801d6c357SChunming Zhou 	fence = dma_fence_get(rcu_dereference_protected(syncobj->fence, 1));
27901d6c357SChunming Zhou 	if (!fence || dma_fence_chain_find_seqno(&fence, wait->point)) {
28001d6c357SChunming Zhou 		dma_fence_put(fence);
28161a98b1bSChristian König 		list_add_tail(&wait->node, &syncobj->cb_list);
28201d6c357SChunming Zhou 	} else if (!fence) {
28301d6c357SChunming Zhou 		wait->fence = dma_fence_get_stub();
28401d6c357SChunming Zhou 	} else {
28501d6c357SChunming Zhou 		wait->fence = fence;
28601d6c357SChunming Zhou 	}
287131280a1SEric Anholt 	spin_unlock(&syncobj->lock);
28848197bc5SChunming Zhou }
28948197bc5SChunming Zhou 
drm_syncobj_remove_wait(struct drm_syncobj * syncobj,struct syncobj_wait_entry * wait)29061a98b1bSChristian König static void drm_syncobj_remove_wait(struct drm_syncobj *syncobj,
29161a98b1bSChristian König 				    struct syncobj_wait_entry *wait)
292131280a1SEric Anholt {
29361a98b1bSChristian König 	if (!wait->node.next)
29461a98b1bSChristian König 		return;
29561a98b1bSChristian König 
296131280a1SEric Anholt 	spin_lock(&syncobj->lock);
29761a98b1bSChristian König 	list_del_init(&wait->node);
298131280a1SEric Anholt 	spin_unlock(&syncobj->lock);
29948197bc5SChunming Zhou }
300131280a1SEric Anholt 
301c7a47229SSimon Ser static void
syncobj_eventfd_entry_free(struct syncobj_eventfd_entry * entry)302c7a47229SSimon Ser syncobj_eventfd_entry_free(struct syncobj_eventfd_entry *entry)
303c7a47229SSimon Ser {
304c7a47229SSimon Ser 	eventfd_ctx_put(entry->ev_fd_ctx);
305c7a47229SSimon Ser 	dma_fence_put(entry->fence);
306c7a47229SSimon Ser 	/* This happens either inside the syncobj lock, or after the node has
307c7a47229SSimon Ser 	 * already been removed from the list.
308c7a47229SSimon Ser 	 */
309c7a47229SSimon Ser 	list_del(&entry->node);
310c7a47229SSimon Ser 	kfree(entry);
311c7a47229SSimon Ser }
312c7a47229SSimon Ser 
313c7a47229SSimon Ser static void
drm_syncobj_add_eventfd(struct drm_syncobj * syncobj,struct syncobj_eventfd_entry * entry)314c7a47229SSimon Ser drm_syncobj_add_eventfd(struct drm_syncobj *syncobj,
315c7a47229SSimon Ser 			struct syncobj_eventfd_entry *entry)
316c7a47229SSimon Ser {
317c7a47229SSimon Ser 	spin_lock(&syncobj->lock);
318c7a47229SSimon Ser 	list_add_tail(&entry->node, &syncobj->ev_fd_list);
319c7a47229SSimon Ser 	syncobj_eventfd_entry_func(syncobj, entry);
320c7a47229SSimon Ser 	spin_unlock(&syncobj->lock);
321c7a47229SSimon Ser }
322c7a47229SSimon Ser 
323e9083420SDave Airlie /**
32444f8a139SChristian König  * drm_syncobj_add_point - add new timeline point to the syncobj
32544f8a139SChristian König  * @syncobj: sync object to add timeline point do
32644f8a139SChristian König  * @chain: chain node to use to add the point
32744f8a139SChristian König  * @fence: fence to encapsulate in the chain node
32844f8a139SChristian König  * @point: sequence number to use for the point
32944f8a139SChristian König  *
33044f8a139SChristian König  * Add the chain node as new timeline point to the syncobj.
33144f8a139SChristian König  */
drm_syncobj_add_point(struct drm_syncobj * syncobj,struct dma_fence_chain * chain,struct dma_fence * fence,uint64_t point)33244f8a139SChristian König void drm_syncobj_add_point(struct drm_syncobj *syncobj,
33344f8a139SChristian König 			   struct dma_fence_chain *chain,
33444f8a139SChristian König 			   struct dma_fence *fence,
33544f8a139SChristian König 			   uint64_t point)
33644f8a139SChristian König {
337c7a47229SSimon Ser 	struct syncobj_wait_entry *wait_cur, *wait_tmp;
338c7a47229SSimon Ser 	struct syncobj_eventfd_entry *ev_fd_cur, *ev_fd_tmp;
33944f8a139SChristian König 	struct dma_fence *prev;
34044f8a139SChristian König 
34144f8a139SChristian König 	dma_fence_get(fence);
34244f8a139SChristian König 
34344f8a139SChristian König 	spin_lock(&syncobj->lock);
34444f8a139SChristian König 
34544f8a139SChristian König 	prev = drm_syncobj_fence_get(syncobj);
34644f8a139SChristian König 	/* You are adding an unorder point to timeline, which could cause payload returned from query_ioctl is 0! */
34744f8a139SChristian König 	if (prev && prev->seqno >= point)
34870eca5d5SDaniel Vetter 		DRM_DEBUG("You are adding an unorder point to timeline!\n");
34944f8a139SChristian König 	dma_fence_chain_init(chain, prev, fence, point);
35044f8a139SChristian König 	rcu_assign_pointer(syncobj->fence, &chain->base);
35144f8a139SChristian König 
352c7a47229SSimon Ser 	list_for_each_entry_safe(wait_cur, wait_tmp, &syncobj->cb_list, node)
353c7a47229SSimon Ser 		syncobj_wait_syncobj_func(syncobj, wait_cur);
354c7a47229SSimon Ser 	list_for_each_entry_safe(ev_fd_cur, ev_fd_tmp, &syncobj->ev_fd_list, node)
355c7a47229SSimon Ser 		syncobj_eventfd_entry_func(syncobj, ev_fd_cur);
35644f8a139SChristian König 	spin_unlock(&syncobj->lock);
35744f8a139SChristian König 
35844f8a139SChristian König 	/* Walk the chain once to trigger garbage collection */
35944f8a139SChristian König 	dma_fence_chain_for_each(fence, prev);
36044f8a139SChristian König 	dma_fence_put(prev);
36144f8a139SChristian König }
36244f8a139SChristian König EXPORT_SYMBOL(drm_syncobj_add_point);
36344f8a139SChristian König 
36444f8a139SChristian König /**
365e9083420SDave Airlie  * drm_syncobj_replace_fence - replace fence in a sync object.
366e9083420SDave Airlie  * @syncobj: Sync object to replace fence in
367e9083420SDave Airlie  * @fence: fence to install in sync file.
368e9083420SDave Airlie  *
3690b258ed1SChristian König  * This replaces the fence on a sync object.
370e9083420SDave Airlie  */
drm_syncobj_replace_fence(struct drm_syncobj * syncobj,struct dma_fence * fence)37100fc2c26SChris Wilson void drm_syncobj_replace_fence(struct drm_syncobj *syncobj,
372e9083420SDave Airlie 			       struct dma_fence *fence)
373e9083420SDave Airlie {
374131280a1SEric Anholt 	struct dma_fence *old_fence;
375c7a47229SSimon Ser 	struct syncobj_wait_entry *wait_cur, *wait_tmp;
376c7a47229SSimon Ser 	struct syncobj_eventfd_entry *ev_fd_cur, *ev_fd_tmp;
377e9083420SDave Airlie 
378131280a1SEric Anholt 	if (fence)
379131280a1SEric Anholt 		dma_fence_get(fence);
380131280a1SEric Anholt 
381131280a1SEric Anholt 	spin_lock(&syncobj->lock);
382131280a1SEric Anholt 
383131280a1SEric Anholt 	old_fence = rcu_dereference_protected(syncobj->fence,
384131280a1SEric Anholt 					      lockdep_is_held(&syncobj->lock));
385131280a1SEric Anholt 	rcu_assign_pointer(syncobj->fence, fence);
386131280a1SEric Anholt 
387131280a1SEric Anholt 	if (fence != old_fence) {
388c7a47229SSimon Ser 		list_for_each_entry_safe(wait_cur, wait_tmp, &syncobj->cb_list, node)
389c7a47229SSimon Ser 			syncobj_wait_syncobj_func(syncobj, wait_cur);
390c7a47229SSimon Ser 		list_for_each_entry_safe(ev_fd_cur, ev_fd_tmp, &syncobj->ev_fd_list, node)
391c7a47229SSimon Ser 			syncobj_eventfd_entry_func(syncobj, ev_fd_cur);
3929c19fb10SJason Ekstrand 	}
393131280a1SEric Anholt 
394131280a1SEric Anholt 	spin_unlock(&syncobj->lock);
395131280a1SEric Anholt 
396131280a1SEric Anholt 	dma_fence_put(old_fence);
397e9083420SDave Airlie }
398e9083420SDave Airlie EXPORT_SYMBOL(drm_syncobj_replace_fence);
399e9083420SDave Airlie 
40086bbd89dSChristian König /**
40186bbd89dSChristian König  * drm_syncobj_assign_null_handle - assign a stub fence to the sync object
40286bbd89dSChristian König  * @syncobj: sync object to assign the fence on
40386bbd89dSChristian König  *
40486bbd89dSChristian König  * Assign a already signaled stub fence to the sync object.
40586bbd89dSChristian König  */
drm_syncobj_assign_null_handle(struct drm_syncobj * syncobj)406fd921693SDavid Stevens static int drm_syncobj_assign_null_handle(struct drm_syncobj *syncobj)
4071fc08218SJason Ekstrand {
408f781f661SChristian König 	struct dma_fence *fence = dma_fence_allocate_private_stub(ktime_get());
409fd921693SDavid Stevens 
41000ae1491SDan Carpenter 	if (!fence)
41100ae1491SDan Carpenter 		return -ENOMEM;
4121fc08218SJason Ekstrand 
4130b258ed1SChristian König 	drm_syncobj_replace_fence(syncobj, fence);
41486bbd89dSChristian König 	dma_fence_put(fence);
415fd921693SDavid Stevens 	return 0;
4161fc08218SJason Ekstrand }
4171fc08218SJason Ekstrand 
418bc9c80feSChristian König /* 5s default for wait submission */
419bc9c80feSChristian König #define DRM_SYNCOBJ_WAIT_FOR_SUBMIT_TIMEOUT 5000000000ULL
420924fe8dfSDaniel Vetter /**
421924fe8dfSDaniel Vetter  * drm_syncobj_find_fence - lookup and reference the fence in a sync object
422924fe8dfSDaniel Vetter  * @file_private: drm file private pointer
423924fe8dfSDaniel Vetter  * @handle: sync object handle to lookup.
4240a6730eaSChunming Zhou  * @point: timeline point
425871edc96SChunming Zhou  * @flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or not
426924fe8dfSDaniel Vetter  * @fence: out parameter for the fence
427924fe8dfSDaniel Vetter  *
428924fe8dfSDaniel Vetter  * This is just a convenience function that combines drm_syncobj_find() and
429131280a1SEric Anholt  * drm_syncobj_fence_get().
430924fe8dfSDaniel Vetter  *
431924fe8dfSDaniel Vetter  * Returns 0 on success or a negative error value on failure. On success @fence
432924fe8dfSDaniel Vetter  * contains a reference to the fence, which must be released by calling
433924fe8dfSDaniel Vetter  * dma_fence_put().
434924fe8dfSDaniel Vetter  */
drm_syncobj_find_fence(struct drm_file * file_private,u32 handle,u64 point,u64 flags,struct dma_fence ** fence)435afaf5923SJason Ekstrand int drm_syncobj_find_fence(struct drm_file *file_private,
436649fdce2SChunming Zhou 			   u32 handle, u64 point, u64 flags,
437e9083420SDave Airlie 			   struct dma_fence **fence)
438e9083420SDave Airlie {
439e9083420SDave Airlie 	struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);
440bc9c80feSChristian König 	struct syncobj_wait_entry wait;
441bc9c80feSChristian König 	u64 timeout = nsecs_to_jiffies64(DRM_SYNCOBJ_WAIT_FOR_SUBMIT_TIMEOUT);
442bc9c80feSChristian König 	int ret;
443e9083420SDave Airlie 
44418226ba5SErik Kurzinger 	if (flags & ~DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT)
44518226ba5SErik Kurzinger 		return -EINVAL;
44618226ba5SErik Kurzinger 
447131280a1SEric Anholt 	if (!syncobj)
448131280a1SEric Anholt 		return -ENOENT;
449131280a1SEric Anholt 
4507621350cSChristian König 	/* Waiting for userspace with locks help is illegal cause that can
4517621350cSChristian König 	 * trivial deadlock with page faults for example. Make lockdep complain
4527621350cSChristian König 	 * about it early on.
4537621350cSChristian König 	 */
4547621350cSChristian König 	if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) {
4557621350cSChristian König 		might_sleep();
4567621350cSChristian König 		lockdep_assert_none_held_once();
4577621350cSChristian König 	}
4587621350cSChristian König 
459131280a1SEric Anholt 	*fence = drm_syncobj_fence_get(syncobj);
460bc9c80feSChristian König 
461bc9c80feSChristian König 	if (*fence) {
462bc9c80feSChristian König 		ret = dma_fence_chain_find_seqno(fence, point);
463b19926d4SBas Nieuwenhuizen 		if (!ret) {
464b19926d4SBas Nieuwenhuizen 			/* If the requested seqno is already signaled
465b19926d4SBas Nieuwenhuizen 			 * drm_syncobj_find_fence may return a NULL
466b19926d4SBas Nieuwenhuizen 			 * fence. To make sure the recipient gets
467b19926d4SBas Nieuwenhuizen 			 * signalled, use a new fence instead.
468b19926d4SBas Nieuwenhuizen 			 */
469b19926d4SBas Nieuwenhuizen 			if (!*fence)
470b19926d4SBas Nieuwenhuizen 				*fence = dma_fence_get_stub();
471b19926d4SBas Nieuwenhuizen 
472a37eef63SDaniel Vetter 			goto out;
473b19926d4SBas Nieuwenhuizen 		}
474bc9c80feSChristian König 		dma_fence_put(*fence);
475bc9c80feSChristian König 	} else {
476131280a1SEric Anholt 		ret = -EINVAL;
477131280a1SEric Anholt 	}
478bc9c80feSChristian König 
479bc9c80feSChristian König 	if (!(flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT))
480a37eef63SDaniel Vetter 		goto out;
481bc9c80feSChristian König 
482bc9c80feSChristian König 	memset(&wait, 0, sizeof(wait));
483bc9c80feSChristian König 	wait.task = current;
484bc9c80feSChristian König 	wait.point = point;
485bc9c80feSChristian König 	drm_syncobj_fence_add_wait(syncobj, &wait);
486bc9c80feSChristian König 
487bc9c80feSChristian König 	do {
488bc9c80feSChristian König 		set_current_state(TASK_INTERRUPTIBLE);
489bc9c80feSChristian König 		if (wait.fence) {
490bc9c80feSChristian König 			ret = 0;
491bc9c80feSChristian König 			break;
492bc9c80feSChristian König 		}
493bc9c80feSChristian König                 if (timeout == 0) {
494bc9c80feSChristian König                         ret = -ETIME;
495bc9c80feSChristian König                         break;
496bc9c80feSChristian König                 }
497bc9c80feSChristian König 
498bc9c80feSChristian König 		if (signal_pending(current)) {
499bc9c80feSChristian König 			ret = -ERESTARTSYS;
500bc9c80feSChristian König 			break;
501bc9c80feSChristian König 		}
502bc9c80feSChristian König 
503bc9c80feSChristian König                 timeout = schedule_timeout(timeout);
504bc9c80feSChristian König 	} while (1);
505bc9c80feSChristian König 
506bc9c80feSChristian König 	__set_current_state(TASK_RUNNING);
507bc9c80feSChristian König 	*fence = wait.fence;
508bc9c80feSChristian König 
509bc9c80feSChristian König 	if (wait.node.next)
510bc9c80feSChristian König 		drm_syncobj_remove_wait(syncobj, &wait);
511bc9c80feSChristian König 
512a37eef63SDaniel Vetter out:
513a37eef63SDaniel Vetter 	drm_syncobj_put(syncobj);
514a37eef63SDaniel Vetter 
515e9083420SDave Airlie 	return ret;
516e9083420SDave Airlie }
517afaf5923SJason Ekstrand EXPORT_SYMBOL(drm_syncobj_find_fence);
518e9083420SDave Airlie 
519e9083420SDave Airlie /**
520e9083420SDave Airlie  * drm_syncobj_free - free a sync object.
521e9083420SDave Airlie  * @kref: kref to free.
522e9083420SDave Airlie  *
523e9083420SDave Airlie  * Only to be called from kref_put in drm_syncobj_put.
524e9083420SDave Airlie  */
drm_syncobj_free(struct kref * kref)525e9083420SDave Airlie void drm_syncobj_free(struct kref *kref)
526e9083420SDave Airlie {
527e9083420SDave Airlie 	struct drm_syncobj *syncobj = container_of(kref,
528e9083420SDave Airlie 						   struct drm_syncobj,
529e9083420SDave Airlie 						   refcount);
530c7a47229SSimon Ser 	struct syncobj_eventfd_entry *ev_fd_cur, *ev_fd_tmp;
531c7a47229SSimon Ser 
5320b258ed1SChristian König 	drm_syncobj_replace_fence(syncobj, NULL);
533c7a47229SSimon Ser 
534c7a47229SSimon Ser 	list_for_each_entry_safe(ev_fd_cur, ev_fd_tmp, &syncobj->ev_fd_list, node)
535c7a47229SSimon Ser 		syncobj_eventfd_entry_free(ev_fd_cur);
536c7a47229SSimon Ser 
537e9083420SDave Airlie 	kfree(syncobj);
538e9083420SDave Airlie }
539e9083420SDave Airlie EXPORT_SYMBOL(drm_syncobj_free);
540e9083420SDave Airlie 
5411321fd2cSMarek Olšák /**
5421321fd2cSMarek Olšák  * drm_syncobj_create - create a new syncobj
5431321fd2cSMarek Olšák  * @out_syncobj: returned syncobj
5441321fd2cSMarek Olšák  * @flags: DRM_SYNCOBJ_* flags
5451321fd2cSMarek Olšák  * @fence: if non-NULL, the syncobj will represent this fence
546924fe8dfSDaniel Vetter  *
547924fe8dfSDaniel Vetter  * This is the first function to create a sync object. After creating, drivers
548924fe8dfSDaniel Vetter  * probably want to make it available to userspace, either through
549924fe8dfSDaniel Vetter  * drm_syncobj_get_handle() or drm_syncobj_get_fd().
550924fe8dfSDaniel Vetter  *
551924fe8dfSDaniel Vetter  * Returns 0 on success or a negative error value on failure.
5521321fd2cSMarek Olšák  */
drm_syncobj_create(struct drm_syncobj ** out_syncobj,uint32_t flags,struct dma_fence * fence)5531321fd2cSMarek Olšák int drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags,
5541321fd2cSMarek Olšák 		       struct dma_fence *fence)
555e9083420SDave Airlie {
556fd921693SDavid Stevens 	int ret;
557e9083420SDave Airlie 	struct drm_syncobj *syncobj;
558e9083420SDave Airlie 
559e9083420SDave Airlie 	syncobj = kzalloc(sizeof(struct drm_syncobj), GFP_KERNEL);
560e9083420SDave Airlie 	if (!syncobj)
561e9083420SDave Airlie 		return -ENOMEM;
562e9083420SDave Airlie 
563e9083420SDave Airlie 	kref_init(&syncobj->refcount);
5649c19fb10SJason Ekstrand 	INIT_LIST_HEAD(&syncobj->cb_list);
565c7a47229SSimon Ser 	INIT_LIST_HEAD(&syncobj->ev_fd_list);
566131280a1SEric Anholt 	spin_lock_init(&syncobj->lock);
567e9083420SDave Airlie 
568fd921693SDavid Stevens 	if (flags & DRM_SYNCOBJ_CREATE_SIGNALED) {
569fd921693SDavid Stevens 		ret = drm_syncobj_assign_null_handle(syncobj);
570fd921693SDavid Stevens 		if (ret < 0) {
571fd921693SDavid Stevens 			drm_syncobj_put(syncobj);
572fd921693SDavid Stevens 			return ret;
573fd921693SDavid Stevens 		}
574fd921693SDavid Stevens 	}
5751fc08218SJason Ekstrand 
5761321fd2cSMarek Olšák 	if (fence)
5770b258ed1SChristian König 		drm_syncobj_replace_fence(syncobj, fence);
5781321fd2cSMarek Olšák 
5791321fd2cSMarek Olšák 	*out_syncobj = syncobj;
5801321fd2cSMarek Olšák 	return 0;
5811321fd2cSMarek Olšák }
5821321fd2cSMarek Olšák EXPORT_SYMBOL(drm_syncobj_create);
5831321fd2cSMarek Olšák 
5841321fd2cSMarek Olšák /**
5851321fd2cSMarek Olšák  * drm_syncobj_get_handle - get a handle from a syncobj
586924fe8dfSDaniel Vetter  * @file_private: drm file private pointer
587924fe8dfSDaniel Vetter  * @syncobj: Sync object to export
588924fe8dfSDaniel Vetter  * @handle: out parameter with the new handle
589924fe8dfSDaniel Vetter  *
590924fe8dfSDaniel Vetter  * Exports a sync object created with drm_syncobj_create() as a handle on
591924fe8dfSDaniel Vetter  * @file_private to userspace.
592924fe8dfSDaniel Vetter  *
593924fe8dfSDaniel Vetter  * Returns 0 on success or a negative error value on failure.
5941321fd2cSMarek Olšák  */
drm_syncobj_get_handle(struct drm_file * file_private,struct drm_syncobj * syncobj,u32 * handle)5951321fd2cSMarek Olšák int drm_syncobj_get_handle(struct drm_file *file_private,
5961321fd2cSMarek Olšák 			   struct drm_syncobj *syncobj, u32 *handle)
5971321fd2cSMarek Olšák {
5981321fd2cSMarek Olšák 	int ret;
5991321fd2cSMarek Olšák 
6001321fd2cSMarek Olšák 	/* take a reference to put in the idr */
6011321fd2cSMarek Olšák 	drm_syncobj_get(syncobj);
6021321fd2cSMarek Olšák 
603e9083420SDave Airlie 	idr_preload(GFP_KERNEL);
604e9083420SDave Airlie 	spin_lock(&file_private->syncobj_table_lock);
605e9083420SDave Airlie 	ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT);
606e9083420SDave Airlie 	spin_unlock(&file_private->syncobj_table_lock);
607e9083420SDave Airlie 
608e9083420SDave Airlie 	idr_preload_end();
609e9083420SDave Airlie 
610e9083420SDave Airlie 	if (ret < 0) {
611e9083420SDave Airlie 		drm_syncobj_put(syncobj);
612e9083420SDave Airlie 		return ret;
613e9083420SDave Airlie 	}
614e9083420SDave Airlie 
615e9083420SDave Airlie 	*handle = ret;
616e9083420SDave Airlie 	return 0;
617e9083420SDave Airlie }
6181321fd2cSMarek Olšák EXPORT_SYMBOL(drm_syncobj_get_handle);
6191321fd2cSMarek Olšák 
drm_syncobj_create_as_handle(struct drm_file * file_private,u32 * handle,uint32_t flags)6201321fd2cSMarek Olšák static int drm_syncobj_create_as_handle(struct drm_file *file_private,
6211321fd2cSMarek Olšák 					u32 *handle, uint32_t flags)
6221321fd2cSMarek Olšák {
6231321fd2cSMarek Olšák 	int ret;
6241321fd2cSMarek Olšák 	struct drm_syncobj *syncobj;
6251321fd2cSMarek Olšák 
6261321fd2cSMarek Olšák 	ret = drm_syncobj_create(&syncobj, flags, NULL);
6271321fd2cSMarek Olšák 	if (ret)
6281321fd2cSMarek Olšák 		return ret;
6291321fd2cSMarek Olšák 
6301321fd2cSMarek Olšák 	ret = drm_syncobj_get_handle(file_private, syncobj, handle);
6311321fd2cSMarek Olšák 	drm_syncobj_put(syncobj);
6321321fd2cSMarek Olšák 	return ret;
6331321fd2cSMarek Olšák }
634e9083420SDave Airlie 
drm_syncobj_destroy(struct drm_file * file_private,u32 handle)635e9083420SDave Airlie static int drm_syncobj_destroy(struct drm_file *file_private,
636e9083420SDave Airlie 			       u32 handle)
637e9083420SDave Airlie {
638e9083420SDave Airlie 	struct drm_syncobj *syncobj;
639e9083420SDave Airlie 
640e9083420SDave Airlie 	spin_lock(&file_private->syncobj_table_lock);
641e9083420SDave Airlie 	syncobj = idr_remove(&file_private->syncobj_idr, handle);
642e9083420SDave Airlie 	spin_unlock(&file_private->syncobj_table_lock);
643e9083420SDave Airlie 
644e9083420SDave Airlie 	if (!syncobj)
645e9083420SDave Airlie 		return -EINVAL;
646e9083420SDave Airlie 
647e9083420SDave Airlie 	drm_syncobj_put(syncobj);
648e9083420SDave Airlie 	return 0;
649e9083420SDave Airlie }
650e9083420SDave Airlie 
drm_syncobj_file_release(struct inode * inode,struct file * file)651e9083420SDave Airlie static int drm_syncobj_file_release(struct inode *inode, struct file *file)
652e9083420SDave Airlie {
653e9083420SDave Airlie 	struct drm_syncobj *syncobj = file->private_data;
654e9083420SDave Airlie 
655e9083420SDave Airlie 	drm_syncobj_put(syncobj);
656e9083420SDave Airlie 	return 0;
657e9083420SDave Airlie }
658e9083420SDave Airlie 
659e9083420SDave Airlie static const struct file_operations drm_syncobj_file_fops = {
660e9083420SDave Airlie 	.release = drm_syncobj_file_release,
661e9083420SDave Airlie };
662e9083420SDave Airlie 
663924fe8dfSDaniel Vetter /**
664924fe8dfSDaniel Vetter  * drm_syncobj_get_fd - get a file descriptor from a syncobj
665924fe8dfSDaniel Vetter  * @syncobj: Sync object to export
666924fe8dfSDaniel Vetter  * @p_fd: out parameter with the new file descriptor
667924fe8dfSDaniel Vetter  *
668924fe8dfSDaniel Vetter  * Exports a sync object created with drm_syncobj_create() as a file descriptor.
669924fe8dfSDaniel Vetter  *
670924fe8dfSDaniel Vetter  * Returns 0 on success or a negative error value on failure.
671924fe8dfSDaniel Vetter  */
drm_syncobj_get_fd(struct drm_syncobj * syncobj,int * p_fd)672684fd0afSMarek Olšák int drm_syncobj_get_fd(struct drm_syncobj *syncobj, int *p_fd)
673684fd0afSMarek Olšák {
674e7cdf5c8SChris Wilson 	struct file *file;
675684fd0afSMarek Olšák 	int fd;
676684fd0afSMarek Olšák 
677684fd0afSMarek Olšák 	fd = get_unused_fd_flags(O_CLOEXEC);
678684fd0afSMarek Olšák 	if (fd < 0)
679684fd0afSMarek Olšák 		return fd;
680684fd0afSMarek Olšák 
681e7cdf5c8SChris Wilson 	file = anon_inode_getfile("syncobj_file",
682e7cdf5c8SChris Wilson 				  &drm_syncobj_file_fops,
683e7cdf5c8SChris Wilson 				  syncobj, 0);
684e7cdf5c8SChris Wilson 	if (IS_ERR(file)) {
685684fd0afSMarek Olšák 		put_unused_fd(fd);
686e7cdf5c8SChris Wilson 		return PTR_ERR(file);
687684fd0afSMarek Olšák 	}
688e7cdf5c8SChris Wilson 
689e7cdf5c8SChris Wilson 	drm_syncobj_get(syncobj);
690e7cdf5c8SChris Wilson 	fd_install(fd, file);
691e7cdf5c8SChris Wilson 
692684fd0afSMarek Olšák 	*p_fd = fd;
693684fd0afSMarek Olšák 	return 0;
694684fd0afSMarek Olšák }
695684fd0afSMarek Olšák EXPORT_SYMBOL(drm_syncobj_get_fd);
696684fd0afSMarek Olšák 
drm_syncobj_handle_to_fd(struct drm_file * file_private,u32 handle,int * p_fd)697e9083420SDave Airlie static int drm_syncobj_handle_to_fd(struct drm_file *file_private,
698e9083420SDave Airlie 				    u32 handle, int *p_fd)
699e9083420SDave Airlie {
700e9083420SDave Airlie 	struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);
701e9083420SDave Airlie 	int ret;
702e9083420SDave Airlie 
703e9083420SDave Airlie 	if (!syncobj)
704e9083420SDave Airlie 		return -EINVAL;
705e9083420SDave Airlie 
706684fd0afSMarek Olšák 	ret = drm_syncobj_get_fd(syncobj, p_fd);
707e9083420SDave Airlie 	drm_syncobj_put(syncobj);
708e9083420SDave Airlie 	return ret;
709e9083420SDave Airlie }
710e9083420SDave Airlie 
drm_syncobj_fd_to_handle(struct drm_file * file_private,int fd,u32 * handle)711e9083420SDave Airlie static int drm_syncobj_fd_to_handle(struct drm_file *file_private,
712e9083420SDave Airlie 				    int fd, u32 *handle)
713e9083420SDave Airlie {
714e7cdf5c8SChris Wilson 	struct drm_syncobj *syncobj;
715*6348be02SAl Viro 	CLASS(fd, f)(fd);
716e9083420SDave Airlie 	int ret;
717e9083420SDave Airlie 
718*6348be02SAl Viro 	if (fd_empty(f))
719e9083420SDave Airlie 		return -EINVAL;
720e9083420SDave Airlie 
721*6348be02SAl Viro 	if (fd_file(f)->f_op != &drm_syncobj_file_fops)
722e7cdf5c8SChris Wilson 		return -EINVAL;
723e7cdf5c8SChris Wilson 
724e9083420SDave Airlie 	/* take a reference to put in the idr */
7251da91ea8SAl Viro 	syncobj = fd_file(f)->private_data;
726e9083420SDave Airlie 	drm_syncobj_get(syncobj);
727e9083420SDave Airlie 
728e9083420SDave Airlie 	idr_preload(GFP_KERNEL);
729e9083420SDave Airlie 	spin_lock(&file_private->syncobj_table_lock);
730e9083420SDave Airlie 	ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT);
731e9083420SDave Airlie 	spin_unlock(&file_private->syncobj_table_lock);
732e9083420SDave Airlie 	idr_preload_end();
733e9083420SDave Airlie 
734e7cdf5c8SChris Wilson 	if (ret > 0) {
735e9083420SDave Airlie 		*handle = ret;
736e7cdf5c8SChris Wilson 		ret = 0;
737e7cdf5c8SChris Wilson 	} else
738e7cdf5c8SChris Wilson 		drm_syncobj_put(syncobj);
739e7cdf5c8SChris Wilson 
740e7cdf5c8SChris Wilson 	return ret;
741e9083420SDave Airlie }
742e9083420SDave Airlie 
drm_syncobj_import_sync_file_fence(struct drm_file * file_private,int fd,int handle)743a32c94afSVille Syrjälä static int drm_syncobj_import_sync_file_fence(struct drm_file *file_private,
7443ee45a3bSDave Airlie 					      int fd, int handle)
7453ee45a3bSDave Airlie {
7463ee45a3bSDave Airlie 	struct dma_fence *fence = sync_file_get_fence(fd);
7473ee45a3bSDave Airlie 	struct drm_syncobj *syncobj;
7483ee45a3bSDave Airlie 
7493ee45a3bSDave Airlie 	if (!fence)
7503ee45a3bSDave Airlie 		return -EINVAL;
7513ee45a3bSDave Airlie 
7523ee45a3bSDave Airlie 	syncobj = drm_syncobj_find(file_private, handle);
7533ee45a3bSDave Airlie 	if (!syncobj) {
7543ee45a3bSDave Airlie 		dma_fence_put(fence);
7553ee45a3bSDave Airlie 		return -ENOENT;
7563ee45a3bSDave Airlie 	}
7573ee45a3bSDave Airlie 
7580b258ed1SChristian König 	drm_syncobj_replace_fence(syncobj, fence);
7593ee45a3bSDave Airlie 	dma_fence_put(fence);
7603ee45a3bSDave Airlie 	drm_syncobj_put(syncobj);
7613ee45a3bSDave Airlie 	return 0;
7623ee45a3bSDave Airlie }
7633ee45a3bSDave Airlie 
drm_syncobj_export_sync_file(struct drm_file * file_private,int handle,int * p_fd)764a32c94afSVille Syrjälä static int drm_syncobj_export_sync_file(struct drm_file *file_private,
7653ee45a3bSDave Airlie 					int handle, int *p_fd)
7663ee45a3bSDave Airlie {
7673ee45a3bSDave Airlie 	int ret;
7683ee45a3bSDave Airlie 	struct dma_fence *fence;
7693ee45a3bSDave Airlie 	struct sync_file *sync_file;
7703ee45a3bSDave Airlie 	int fd = get_unused_fd_flags(O_CLOEXEC);
7713ee45a3bSDave Airlie 
7723ee45a3bSDave Airlie 	if (fd < 0)
7733ee45a3bSDave Airlie 		return fd;
7743ee45a3bSDave Airlie 
775649fdce2SChunming Zhou 	ret = drm_syncobj_find_fence(file_private, handle, 0, 0, &fence);
7763ee45a3bSDave Airlie 	if (ret)
7773ee45a3bSDave Airlie 		goto err_put_fd;
7783ee45a3bSDave Airlie 
7793ee45a3bSDave Airlie 	sync_file = sync_file_create(fence);
7803ee45a3bSDave Airlie 
7813ee45a3bSDave Airlie 	dma_fence_put(fence);
7823ee45a3bSDave Airlie 
7833ee45a3bSDave Airlie 	if (!sync_file) {
7843ee45a3bSDave Airlie 		ret = -EINVAL;
7853ee45a3bSDave Airlie 		goto err_put_fd;
7863ee45a3bSDave Airlie 	}
7873ee45a3bSDave Airlie 
7883ee45a3bSDave Airlie 	fd_install(fd, sync_file->file);
7893ee45a3bSDave Airlie 
7903ee45a3bSDave Airlie 	*p_fd = fd;
7913ee45a3bSDave Airlie 	return 0;
7923ee45a3bSDave Airlie err_put_fd:
7933ee45a3bSDave Airlie 	put_unused_fd(fd);
7943ee45a3bSDave Airlie 	return ret;
7953ee45a3bSDave Airlie }
796e9083420SDave Airlie /**
7970ae865efSCai Huoqing  * drm_syncobj_open - initializes syncobj file-private structures at devnode open time
798e9083420SDave Airlie  * @file_private: drm file-private structure to set up
799e9083420SDave Airlie  *
800e9083420SDave Airlie  * Called at device open time, sets up the structure for handling refcounting
801e9083420SDave Airlie  * of sync objects.
802e9083420SDave Airlie  */
803e9083420SDave Airlie void
drm_syncobj_open(struct drm_file * file_private)804e9083420SDave Airlie drm_syncobj_open(struct drm_file *file_private)
805e9083420SDave Airlie {
806e86584c5SChris Wilson 	idr_init_base(&file_private->syncobj_idr, 1);
807e9083420SDave Airlie 	spin_lock_init(&file_private->syncobj_table_lock);
808e9083420SDave Airlie }
809e9083420SDave Airlie 
810e9083420SDave Airlie static int
drm_syncobj_release_handle(int id,void * ptr,void * data)811e9083420SDave Airlie drm_syncobj_release_handle(int id, void *ptr, void *data)
812e9083420SDave Airlie {
813e9083420SDave Airlie 	struct drm_syncobj *syncobj = ptr;
814e9083420SDave Airlie 
815e9083420SDave Airlie 	drm_syncobj_put(syncobj);
816e9083420SDave Airlie 	return 0;
817e9083420SDave Airlie }
818e9083420SDave Airlie 
819e9083420SDave Airlie /**
820e9083420SDave Airlie  * drm_syncobj_release - release file-private sync object resources
821e9083420SDave Airlie  * @file_private: drm file-private structure to clean up
822e9083420SDave Airlie  *
823e9083420SDave Airlie  * Called at close time when the filp is going away.
824e9083420SDave Airlie  *
825e9083420SDave Airlie  * Releases any remaining references on objects by this filp.
826e9083420SDave Airlie  */
827e9083420SDave Airlie void
drm_syncobj_release(struct drm_file * file_private)828e9083420SDave Airlie drm_syncobj_release(struct drm_file *file_private)
829e9083420SDave Airlie {
830e9083420SDave Airlie 	idr_for_each(&file_private->syncobj_idr,
831e9083420SDave Airlie 		     &drm_syncobj_release_handle, file_private);
832e9083420SDave Airlie 	idr_destroy(&file_private->syncobj_idr);
833e9083420SDave Airlie }
834e9083420SDave Airlie 
835e9083420SDave Airlie int
drm_syncobj_create_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)836e9083420SDave Airlie drm_syncobj_create_ioctl(struct drm_device *dev, void *data,
837e9083420SDave Airlie 			 struct drm_file *file_private)
838e9083420SDave Airlie {
839e9083420SDave Airlie 	struct drm_syncobj_create *args = data;
840e9083420SDave Airlie 
841e9083420SDave Airlie 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
84269fdf420SChris Wilson 		return -EOPNOTSUPP;
843e9083420SDave Airlie 
844e9083420SDave Airlie 	/* no valid flags yet */
845131280a1SEric Anholt 	if (args->flags & ~DRM_SYNCOBJ_CREATE_SIGNALED)
846e9083420SDave Airlie 		return -EINVAL;
847e9083420SDave Airlie 
8481321fd2cSMarek Olšák 	return drm_syncobj_create_as_handle(file_private,
8491fc08218SJason Ekstrand 					    &args->handle, args->flags);
850e9083420SDave Airlie }
851e9083420SDave Airlie 
852e9083420SDave Airlie int
drm_syncobj_destroy_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)853e9083420SDave Airlie drm_syncobj_destroy_ioctl(struct drm_device *dev, void *data,
854e9083420SDave Airlie 			  struct drm_file *file_private)
855e9083420SDave Airlie {
856e9083420SDave Airlie 	struct drm_syncobj_destroy *args = data;
857e9083420SDave Airlie 
858e9083420SDave Airlie 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
85969fdf420SChris Wilson 		return -EOPNOTSUPP;
860e9083420SDave Airlie 
861e9083420SDave Airlie 	/* make sure padding is empty */
862e9083420SDave Airlie 	if (args->pad)
863e9083420SDave Airlie 		return -EINVAL;
864e9083420SDave Airlie 	return drm_syncobj_destroy(file_private, args->handle);
865e9083420SDave Airlie }
866e9083420SDave Airlie 
867e9083420SDave Airlie int
drm_syncobj_handle_to_fd_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)868e9083420SDave Airlie drm_syncobj_handle_to_fd_ioctl(struct drm_device *dev, void *data,
869e9083420SDave Airlie 				   struct drm_file *file_private)
870e9083420SDave Airlie {
871e9083420SDave Airlie 	struct drm_syncobj_handle *args = data;
872e9083420SDave Airlie 
873e9083420SDave Airlie 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
87469fdf420SChris Wilson 		return -EOPNOTSUPP;
875e9083420SDave Airlie 
8763ee45a3bSDave Airlie 	if (args->pad)
877e9083420SDave Airlie 		return -EINVAL;
878e9083420SDave Airlie 
8793ee45a3bSDave Airlie 	if (args->flags != 0 &&
8803ee45a3bSDave Airlie 	    args->flags != DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE)
8813ee45a3bSDave Airlie 		return -EINVAL;
8823ee45a3bSDave Airlie 
8833ee45a3bSDave Airlie 	if (args->flags & DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE)
8843ee45a3bSDave Airlie 		return drm_syncobj_export_sync_file(file_private, args->handle,
8853ee45a3bSDave Airlie 						    &args->fd);
8863ee45a3bSDave Airlie 
887e9083420SDave Airlie 	return drm_syncobj_handle_to_fd(file_private, args->handle,
888e9083420SDave Airlie 					&args->fd);
889e9083420SDave Airlie }
890e9083420SDave Airlie 
891e9083420SDave Airlie int
drm_syncobj_fd_to_handle_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)892e9083420SDave Airlie drm_syncobj_fd_to_handle_ioctl(struct drm_device *dev, void *data,
893e9083420SDave Airlie 				   struct drm_file *file_private)
894e9083420SDave Airlie {
895e9083420SDave Airlie 	struct drm_syncobj_handle *args = data;
896e9083420SDave Airlie 
897e9083420SDave Airlie 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
89869fdf420SChris Wilson 		return -EOPNOTSUPP;
899e9083420SDave Airlie 
9003ee45a3bSDave Airlie 	if (args->pad)
901e9083420SDave Airlie 		return -EINVAL;
902e9083420SDave Airlie 
9033ee45a3bSDave Airlie 	if (args->flags != 0 &&
9043ee45a3bSDave Airlie 	    args->flags != DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE)
9053ee45a3bSDave Airlie 		return -EINVAL;
9063ee45a3bSDave Airlie 
9073ee45a3bSDave Airlie 	if (args->flags & DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE)
9083ee45a3bSDave Airlie 		return drm_syncobj_import_sync_file_fence(file_private,
9093ee45a3bSDave Airlie 							  args->fd,
9103ee45a3bSDave Airlie 							  args->handle);
9113ee45a3bSDave Airlie 
912e9083420SDave Airlie 	return drm_syncobj_fd_to_handle(file_private, args->fd,
913e9083420SDave Airlie 					&args->handle);
914e9083420SDave Airlie }
9155e60a10eSDave Airlie 
drm_syncobj_transfer_to_timeline(struct drm_file * file_private,struct drm_syncobj_transfer * args)916ea569910SChunming Zhou static int drm_syncobj_transfer_to_timeline(struct drm_file *file_private,
917ea569910SChunming Zhou 					    struct drm_syncobj_transfer *args)
918ea569910SChunming Zhou {
919ea569910SChunming Zhou 	struct drm_syncobj *timeline_syncobj = NULL;
920ec8d985fSChristian König 	struct dma_fence *fence, *tmp;
921ea569910SChunming Zhou 	struct dma_fence_chain *chain;
922ea569910SChunming Zhou 	int ret;
923ea569910SChunming Zhou 
924ea569910SChunming Zhou 	timeline_syncobj = drm_syncobj_find(file_private, args->dst_handle);
925ea569910SChunming Zhou 	if (!timeline_syncobj) {
926ea569910SChunming Zhou 		return -ENOENT;
927ea569910SChunming Zhou 	}
928ea569910SChunming Zhou 	ret = drm_syncobj_find_fence(file_private, args->src_handle,
929ea569910SChunming Zhou 				     args->src_point, args->flags,
930ec8d985fSChristian König 				     &tmp);
931ea569910SChunming Zhou 	if (ret)
932721255b5SChristian König 		goto err_put_timeline;
933721255b5SChristian König 
934ec8d985fSChristian König 	fence = dma_fence_unwrap_merge(tmp);
935ec8d985fSChristian König 	dma_fence_put(tmp);
936f4e3a12bSYang Yingliang 	if (!fence) {
937f4e3a12bSYang Yingliang 		ret = -ENOMEM;
938ec8d985fSChristian König 		goto err_put_timeline;
939f4e3a12bSYang Yingliang 	}
940721255b5SChristian König 
941440d0f12SChristian König 	chain = dma_fence_chain_alloc();
942ea569910SChunming Zhou 	if (!chain) {
943ea569910SChunming Zhou 		ret = -ENOMEM;
944721255b5SChristian König 		goto err_free_fence;
945ea569910SChunming Zhou 	}
946721255b5SChristian König 
947ea569910SChunming Zhou 	drm_syncobj_add_point(timeline_syncobj, chain, fence, args->dst_point);
948721255b5SChristian König err_free_fence:
949ea569910SChunming Zhou 	dma_fence_put(fence);
950721255b5SChristian König err_put_timeline:
951ea569910SChunming Zhou 	drm_syncobj_put(timeline_syncobj);
952ea569910SChunming Zhou 
953ea569910SChunming Zhou 	return ret;
954ea569910SChunming Zhou }
955ea569910SChunming Zhou 
956ea569910SChunming Zhou static int
drm_syncobj_transfer_to_binary(struct drm_file * file_private,struct drm_syncobj_transfer * args)957ea569910SChunming Zhou drm_syncobj_transfer_to_binary(struct drm_file *file_private,
958ea569910SChunming Zhou 			       struct drm_syncobj_transfer *args)
959ea569910SChunming Zhou {
960ea569910SChunming Zhou 	struct drm_syncobj *binary_syncobj = NULL;
961ea569910SChunming Zhou 	struct dma_fence *fence;
962ea569910SChunming Zhou 	int ret;
963ea569910SChunming Zhou 
964ea569910SChunming Zhou 	binary_syncobj = drm_syncobj_find(file_private, args->dst_handle);
965ea569910SChunming Zhou 	if (!binary_syncobj)
966ea569910SChunming Zhou 		return -ENOENT;
967ea569910SChunming Zhou 	ret = drm_syncobj_find_fence(file_private, args->src_handle,
968ea569910SChunming Zhou 				     args->src_point, args->flags, &fence);
969ea569910SChunming Zhou 	if (ret)
970ea569910SChunming Zhou 		goto err;
971ea569910SChunming Zhou 	drm_syncobj_replace_fence(binary_syncobj, fence);
972ea569910SChunming Zhou 	dma_fence_put(fence);
973ea569910SChunming Zhou err:
974ea569910SChunming Zhou 	drm_syncobj_put(binary_syncobj);
975ea569910SChunming Zhou 
976ea569910SChunming Zhou 	return ret;
977ea569910SChunming Zhou }
978ea569910SChunming Zhou int
drm_syncobj_transfer_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)979ea569910SChunming Zhou drm_syncobj_transfer_ioctl(struct drm_device *dev, void *data,
980ea569910SChunming Zhou 			   struct drm_file *file_private)
981ea569910SChunming Zhou {
982ea569910SChunming Zhou 	struct drm_syncobj_transfer *args = data;
983ea569910SChunming Zhou 	int ret;
984ea569910SChunming Zhou 
985060cebb2SLionel Landwerlin 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
9865ec77638SLionel Landwerlin 		return -EOPNOTSUPP;
987ea569910SChunming Zhou 
988ea569910SChunming Zhou 	if (args->pad)
989ea569910SChunming Zhou 		return -EINVAL;
990ea569910SChunming Zhou 
991ea569910SChunming Zhou 	if (args->dst_point)
992ea569910SChunming Zhou 		ret = drm_syncobj_transfer_to_timeline(file_private, args);
993ea569910SChunming Zhou 	else
994ea569910SChunming Zhou 		ret = drm_syncobj_transfer_to_binary(file_private, args);
995ea569910SChunming Zhou 
996ea569910SChunming Zhou 	return ret;
997ea569910SChunming Zhou }
998ea569910SChunming Zhou 
syncobj_wait_fence_func(struct dma_fence * fence,struct dma_fence_cb * cb)999e7aca503SJason Ekstrand static void syncobj_wait_fence_func(struct dma_fence *fence,
1000e7aca503SJason Ekstrand 				    struct dma_fence_cb *cb)
1001e7aca503SJason Ekstrand {
1002e7aca503SJason Ekstrand 	struct syncobj_wait_entry *wait =
1003e7aca503SJason Ekstrand 		container_of(cb, struct syncobj_wait_entry, fence_cb);
1004e7aca503SJason Ekstrand 
1005e7aca503SJason Ekstrand 	wake_up_process(wait->task);
1006e7aca503SJason Ekstrand }
1007e7aca503SJason Ekstrand 
syncobj_wait_syncobj_func(struct drm_syncobj * syncobj,struct syncobj_wait_entry * wait)1008e7aca503SJason Ekstrand static void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj,
100961a98b1bSChristian König 				      struct syncobj_wait_entry *wait)
1010e7aca503SJason Ekstrand {
101101d6c357SChunming Zhou 	struct dma_fence *fence;
101201d6c357SChunming Zhou 
1013131280a1SEric Anholt 	/* This happens inside the syncobj lock */
101401d6c357SChunming Zhou 	fence = rcu_dereference_protected(syncobj->fence,
101501d6c357SChunming Zhou 					  lockdep_is_held(&syncobj->lock));
101601d6c357SChunming Zhou 	dma_fence_get(fence);
101701d6c357SChunming Zhou 	if (!fence || dma_fence_chain_find_seqno(&fence, wait->point)) {
101801d6c357SChunming Zhou 		dma_fence_put(fence);
101901d6c357SChunming Zhou 		return;
102001d6c357SChunming Zhou 	} else if (!fence) {
102101d6c357SChunming Zhou 		wait->fence = dma_fence_get_stub();
102201d6c357SChunming Zhou 	} else {
102301d6c357SChunming Zhou 		wait->fence = fence;
102401d6c357SChunming Zhou 	}
102501d6c357SChunming Zhou 
1026e7aca503SJason Ekstrand 	wake_up_process(wait->task);
102701d6c357SChunming Zhou 	list_del_init(&wait->node);
1028e7aca503SJason Ekstrand }
1029e7aca503SJason Ekstrand 
drm_syncobj_array_wait_timeout(struct drm_syncobj ** syncobjs,void __user * user_points,uint32_t count,uint32_t flags,signed long timeout,uint32_t * idx,ktime_t * deadline)1030e7aca503SJason Ekstrand static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs,
103101d6c357SChunming Zhou 						  void __user *user_points,
1032e7aca503SJason Ekstrand 						  uint32_t count,
1033e7aca503SJason Ekstrand 						  uint32_t flags,
1034e7aca503SJason Ekstrand 						  signed long timeout,
10358570c279SRob Clark 						  uint32_t *idx,
10368570c279SRob Clark 						  ktime_t *deadline)
1037e7aca503SJason Ekstrand {
1038e7aca503SJason Ekstrand 	struct syncobj_wait_entry *entries;
1039e7aca503SJason Ekstrand 	struct dma_fence *fence;
104001d6c357SChunming Zhou 	uint64_t *points;
1041e7aca503SJason Ekstrand 	uint32_t signaled_count, i;
1042e7aca503SJason Ekstrand 
10438c44ea81SErik Kurzinger 	if (flags & (DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
1044d3f55248SErik Kurzinger 		     DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE)) {
1045d3f55248SErik Kurzinger 		might_sleep();
10467621350cSChristian König 		lockdep_assert_none_held_once();
1047d3f55248SErik Kurzinger 	}
10487621350cSChristian König 
104901d6c357SChunming Zhou 	points = kmalloc_array(count, sizeof(*points), GFP_KERNEL);
105001d6c357SChunming Zhou 	if (points == NULL)
1051e7aca503SJason Ekstrand 		return -ENOMEM;
1052e7aca503SJason Ekstrand 
105301d6c357SChunming Zhou 	if (!user_points) {
105401d6c357SChunming Zhou 		memset(points, 0, count * sizeof(uint64_t));
105501d6c357SChunming Zhou 
105601d6c357SChunming Zhou 	} else if (copy_from_user(points, user_points,
105701d6c357SChunming Zhou 				  sizeof(uint64_t) * count)) {
105801d6c357SChunming Zhou 		timeout = -EFAULT;
105901d6c357SChunming Zhou 		goto err_free_points;
106001d6c357SChunming Zhou 	}
106101d6c357SChunming Zhou 
106201d6c357SChunming Zhou 	entries = kcalloc(count, sizeof(*entries), GFP_KERNEL);
106301d6c357SChunming Zhou 	if (!entries) {
106401d6c357SChunming Zhou 		timeout = -ENOMEM;
106501d6c357SChunming Zhou 		goto err_free_points;
106601d6c357SChunming Zhou 	}
1067e7aca503SJason Ekstrand 	/* Walk the list of sync objects and initialize entries.  We do
1068e7aca503SJason Ekstrand 	 * this up-front so that we can properly return -EINVAL if there is
1069e7aca503SJason Ekstrand 	 * a syncobj with a missing fence and then never have the chance of
1070e7aca503SJason Ekstrand 	 * returning -EINVAL again.
1071e7aca503SJason Ekstrand 	 */
1072e7aca503SJason Ekstrand 	signaled_count = 0;
1073e7aca503SJason Ekstrand 	for (i = 0; i < count; ++i) {
107401d6c357SChunming Zhou 		struct dma_fence *fence;
107501d6c357SChunming Zhou 
1076e7aca503SJason Ekstrand 		entries[i].task = current;
107701d6c357SChunming Zhou 		entries[i].point = points[i];
107801d6c357SChunming Zhou 		fence = drm_syncobj_fence_get(syncobjs[i]);
107901d6c357SChunming Zhou 		if (!fence || dma_fence_chain_find_seqno(&fence, points[i])) {
108001d6c357SChunming Zhou 			dma_fence_put(fence);
1081101c9f63SErik Kurzinger 			if (flags & (DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
1082101c9f63SErik Kurzinger 				     DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE)) {
1083e7aca503SJason Ekstrand 				continue;
1084e7aca503SJason Ekstrand 			} else {
108512fec62aSChris Wilson 				timeout = -EINVAL;
1086e7aca503SJason Ekstrand 				goto cleanup_entries;
1087e7aca503SJason Ekstrand 			}
1088e7aca503SJason Ekstrand 		}
1089e7aca503SJason Ekstrand 
109001d6c357SChunming Zhou 		if (fence)
109101d6c357SChunming Zhou 			entries[i].fence = fence;
109201d6c357SChunming Zhou 		else
109301d6c357SChunming Zhou 			entries[i].fence = dma_fence_get_stub();
109401d6c357SChunming Zhou 
109501d6c357SChunming Zhou 		if ((flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE) ||
109601d6c357SChunming Zhou 		    dma_fence_is_signaled(entries[i].fence)) {
1097e7aca503SJason Ekstrand 			if (signaled_count == 0 && idx)
1098e7aca503SJason Ekstrand 				*idx = i;
1099e7aca503SJason Ekstrand 			signaled_count++;
1100e7aca503SJason Ekstrand 		}
1101e7aca503SJason Ekstrand 	}
1102e7aca503SJason Ekstrand 
1103e7aca503SJason Ekstrand 	if (signaled_count == count ||
1104e7aca503SJason Ekstrand 	    (signaled_count > 0 &&
1105e7aca503SJason Ekstrand 	     !(flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL)))
1106e7aca503SJason Ekstrand 		goto cleanup_entries;
1107e7aca503SJason Ekstrand 
1108e7aca503SJason Ekstrand 	/* There's a very annoying laxness in the dma_fence API here, in
1109e7aca503SJason Ekstrand 	 * that backends are not required to automatically report when a
1110e7aca503SJason Ekstrand 	 * fence is signaled prior to fence->ops->enable_signaling() being
1111e7aca503SJason Ekstrand 	 * called.  So here if we fail to match signaled_count, we need to
1112e7aca503SJason Ekstrand 	 * fallthough and try a 0 timeout wait!
1113e7aca503SJason Ekstrand 	 */
1114e7aca503SJason Ekstrand 
11158c44ea81SErik Kurzinger 	if (flags & (DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
11168c44ea81SErik Kurzinger 		     DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE)) {
111761a98b1bSChristian König 		for (i = 0; i < count; ++i)
111861a98b1bSChristian König 			drm_syncobj_fence_add_wait(syncobjs[i], &entries[i]);
1119e7aca503SJason Ekstrand 	}
1120e7aca503SJason Ekstrand 
11218570c279SRob Clark 	if (deadline) {
11228570c279SRob Clark 		for (i = 0; i < count; ++i) {
11238570c279SRob Clark 			fence = entries[i].fence;
11248570c279SRob Clark 			if (!fence)
11258570c279SRob Clark 				continue;
11268570c279SRob Clark 			dma_fence_set_deadline(fence, *deadline);
11278570c279SRob Clark 		}
11288570c279SRob Clark 	}
11298570c279SRob Clark 
1130e7aca503SJason Ekstrand 	do {
1131e7aca503SJason Ekstrand 		set_current_state(TASK_INTERRUPTIBLE);
1132e7aca503SJason Ekstrand 
1133e7aca503SJason Ekstrand 		signaled_count = 0;
1134e7aca503SJason Ekstrand 		for (i = 0; i < count; ++i) {
1135e7aca503SJason Ekstrand 			fence = entries[i].fence;
1136e7aca503SJason Ekstrand 			if (!fence)
1137e7aca503SJason Ekstrand 				continue;
1138e7aca503SJason Ekstrand 
113901d6c357SChunming Zhou 			if ((flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE) ||
114001d6c357SChunming Zhou 			    dma_fence_is_signaled(fence) ||
1141e7aca503SJason Ekstrand 			    (!entries[i].fence_cb.func &&
1142e7aca503SJason Ekstrand 			     dma_fence_add_callback(fence,
1143e7aca503SJason Ekstrand 						    &entries[i].fence_cb,
1144e7aca503SJason Ekstrand 						    syncobj_wait_fence_func))) {
1145e7aca503SJason Ekstrand 				/* The fence has been signaled */
1146e7aca503SJason Ekstrand 				if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL) {
1147e7aca503SJason Ekstrand 					signaled_count++;
1148e7aca503SJason Ekstrand 				} else {
1149e7aca503SJason Ekstrand 					if (idx)
1150e7aca503SJason Ekstrand 						*idx = i;
1151e7aca503SJason Ekstrand 					goto done_waiting;
1152e7aca503SJason Ekstrand 				}
1153e7aca503SJason Ekstrand 			}
1154e7aca503SJason Ekstrand 		}
1155e7aca503SJason Ekstrand 
1156e7aca503SJason Ekstrand 		if (signaled_count == count)
1157e7aca503SJason Ekstrand 			goto done_waiting;
1158e7aca503SJason Ekstrand 
1159e7aca503SJason Ekstrand 		if (timeout == 0) {
116012fec62aSChris Wilson 			timeout = -ETIME;
1161e7aca503SJason Ekstrand 			goto done_waiting;
1162e7aca503SJason Ekstrand 		}
1163e7aca503SJason Ekstrand 
116412fec62aSChris Wilson 		if (signal_pending(current)) {
116512fec62aSChris Wilson 			timeout = -ERESTARTSYS;
116612fec62aSChris Wilson 			goto done_waiting;
116712fec62aSChris Wilson 		}
1168e7aca503SJason Ekstrand 
116912fec62aSChris Wilson 		timeout = schedule_timeout(timeout);
117012fec62aSChris Wilson 	} while (1);
1171e7aca503SJason Ekstrand 
1172e7aca503SJason Ekstrand done_waiting:
1173e7aca503SJason Ekstrand 	__set_current_state(TASK_RUNNING);
1174e7aca503SJason Ekstrand 
1175e7aca503SJason Ekstrand cleanup_entries:
1176e7aca503SJason Ekstrand 	for (i = 0; i < count; ++i) {
117761a98b1bSChristian König 		drm_syncobj_remove_wait(syncobjs[i], &entries[i]);
1178e7aca503SJason Ekstrand 		if (entries[i].fence_cb.func)
1179e7aca503SJason Ekstrand 			dma_fence_remove_callback(entries[i].fence,
1180e7aca503SJason Ekstrand 						  &entries[i].fence_cb);
1181e7aca503SJason Ekstrand 		dma_fence_put(entries[i].fence);
1182e7aca503SJason Ekstrand 	}
1183e7aca503SJason Ekstrand 	kfree(entries);
1184e7aca503SJason Ekstrand 
118501d6c357SChunming Zhou err_free_points:
118601d6c357SChunming Zhou 	kfree(points);
118701d6c357SChunming Zhou 
118812fec62aSChris Wilson 	return timeout;
1189e7aca503SJason Ekstrand }
1190e7aca503SJason Ekstrand 
11915e60a10eSDave Airlie /**
11925e60a10eSDave Airlie  * drm_timeout_abs_to_jiffies - calculate jiffies timeout from absolute value
11935e60a10eSDave Airlie  *
11945e60a10eSDave Airlie  * @timeout_nsec: timeout nsec component in ns, 0 for poll
11955e60a10eSDave Airlie  *
11965e60a10eSDave Airlie  * Calculate the timeout in jiffies from an absolute time in sec/nsec.
11975e60a10eSDave Airlie  */
drm_timeout_abs_to_jiffies(int64_t timeout_nsec)1198877b3729SQiang Yu signed long drm_timeout_abs_to_jiffies(int64_t timeout_nsec)
11995e60a10eSDave Airlie {
12005e60a10eSDave Airlie 	ktime_t abs_timeout, now;
12015e60a10eSDave Airlie 	u64 timeout_ns, timeout_jiffies64;
12025e60a10eSDave Airlie 
12035e60a10eSDave Airlie 	/* make 0 timeout means poll - absolute 0 doesn't seem valid */
12045e60a10eSDave Airlie 	if (timeout_nsec == 0)
12055e60a10eSDave Airlie 		return 0;
12065e60a10eSDave Airlie 
12075e60a10eSDave Airlie 	abs_timeout = ns_to_ktime(timeout_nsec);
12085e60a10eSDave Airlie 	now = ktime_get();
12095e60a10eSDave Airlie 
12105e60a10eSDave Airlie 	if (!ktime_after(abs_timeout, now))
12115e60a10eSDave Airlie 		return 0;
12125e60a10eSDave Airlie 
12135e60a10eSDave Airlie 	timeout_ns = ktime_to_ns(ktime_sub(abs_timeout, now));
12145e60a10eSDave Airlie 
12155e60a10eSDave Airlie 	timeout_jiffies64 = nsecs_to_jiffies64(timeout_ns);
12165e60a10eSDave Airlie 	/*  clamp timeout to avoid infinite timeout */
12175e60a10eSDave Airlie 	if (timeout_jiffies64 >= MAX_SCHEDULE_TIMEOUT - 1)
12185e60a10eSDave Airlie 		return MAX_SCHEDULE_TIMEOUT - 1;
12195e60a10eSDave Airlie 
12205e60a10eSDave Airlie 	return timeout_jiffies64 + 1;
12215e60a10eSDave Airlie }
1222877b3729SQiang Yu EXPORT_SYMBOL(drm_timeout_abs_to_jiffies);
12235e60a10eSDave Airlie 
drm_syncobj_array_wait(struct drm_device * dev,struct drm_file * file_private,struct drm_syncobj_wait * wait,struct drm_syncobj_timeline_wait * timeline_wait,struct drm_syncobj ** syncobjs,bool timeline,ktime_t * deadline)1224e7aca503SJason Ekstrand static int drm_syncobj_array_wait(struct drm_device *dev,
12255e60a10eSDave Airlie 				  struct drm_file *file_private,
12265e60a10eSDave Airlie 				  struct drm_syncobj_wait *wait,
122701d6c357SChunming Zhou 				  struct drm_syncobj_timeline_wait *timeline_wait,
12288570c279SRob Clark 				  struct drm_syncobj **syncobjs, bool timeline,
12298570c279SRob Clark 				  ktime_t *deadline)
12305e60a10eSDave Airlie {
123101d6c357SChunming Zhou 	signed long timeout = 0;
12325e60a10eSDave Airlie 	uint32_t first = ~0;
12335e60a10eSDave Airlie 
123401d6c357SChunming Zhou 	if (!timeline) {
123501d6c357SChunming Zhou 		timeout = drm_timeout_abs_to_jiffies(wait->timeout_nsec);
123612fec62aSChris Wilson 		timeout = drm_syncobj_array_wait_timeout(syncobjs,
123701d6c357SChunming Zhou 							 NULL,
12385e60a10eSDave Airlie 							 wait->count_handles,
1239e7aca503SJason Ekstrand 							 wait->flags,
12408570c279SRob Clark 							 timeout, &first,
12418570c279SRob Clark 							 deadline);
124212fec62aSChris Wilson 		if (timeout < 0)
124312fec62aSChris Wilson 			return timeout;
12445e60a10eSDave Airlie 		wait->first_signaled = first;
124501d6c357SChunming Zhou 	} else {
124601d6c357SChunming Zhou 		timeout = drm_timeout_abs_to_jiffies(timeline_wait->timeout_nsec);
124701d6c357SChunming Zhou 		timeout = drm_syncobj_array_wait_timeout(syncobjs,
124801d6c357SChunming Zhou 							 u64_to_user_ptr(timeline_wait->points),
124901d6c357SChunming Zhou 							 timeline_wait->count_handles,
125001d6c357SChunming Zhou 							 timeline_wait->flags,
12518570c279SRob Clark 							 timeout, &first,
12528570c279SRob Clark 							 deadline);
125301d6c357SChunming Zhou 		if (timeout < 0)
125401d6c357SChunming Zhou 			return timeout;
125501d6c357SChunming Zhou 		timeline_wait->first_signaled = first;
125601d6c357SChunming Zhou 	}
12575e60a10eSDave Airlie 	return 0;
12585e60a10eSDave Airlie }
12595e60a10eSDave Airlie 
drm_syncobj_array_find(struct drm_file * file_private,void __user * user_handles,uint32_t count_handles,struct drm_syncobj *** syncobjs_out)12603e6fb72dSJason Ekstrand static int drm_syncobj_array_find(struct drm_file *file_private,
12619e554462SVille Syrjälä 				  void __user *user_handles,
12629e554462SVille Syrjälä 				  uint32_t count_handles,
12633e6fb72dSJason Ekstrand 				  struct drm_syncobj ***syncobjs_out)
12643e6fb72dSJason Ekstrand {
12653e6fb72dSJason Ekstrand 	uint32_t i, *handles;
12663e6fb72dSJason Ekstrand 	struct drm_syncobj **syncobjs;
12673e6fb72dSJason Ekstrand 	int ret;
12683e6fb72dSJason Ekstrand 
12693e6fb72dSJason Ekstrand 	handles = kmalloc_array(count_handles, sizeof(*handles), GFP_KERNEL);
12703e6fb72dSJason Ekstrand 	if (handles == NULL)
12713e6fb72dSJason Ekstrand 		return -ENOMEM;
12723e6fb72dSJason Ekstrand 
12733e6fb72dSJason Ekstrand 	if (copy_from_user(handles, user_handles,
12743e6fb72dSJason Ekstrand 			   sizeof(uint32_t) * count_handles)) {
12753e6fb72dSJason Ekstrand 		ret = -EFAULT;
12763e6fb72dSJason Ekstrand 		goto err_free_handles;
12773e6fb72dSJason Ekstrand 	}
12783e6fb72dSJason Ekstrand 
12793e6fb72dSJason Ekstrand 	syncobjs = kmalloc_array(count_handles, sizeof(*syncobjs), GFP_KERNEL);
12803e6fb72dSJason Ekstrand 	if (syncobjs == NULL) {
12813e6fb72dSJason Ekstrand 		ret = -ENOMEM;
12823e6fb72dSJason Ekstrand 		goto err_free_handles;
12833e6fb72dSJason Ekstrand 	}
12843e6fb72dSJason Ekstrand 
12853e6fb72dSJason Ekstrand 	for (i = 0; i < count_handles; i++) {
12863e6fb72dSJason Ekstrand 		syncobjs[i] = drm_syncobj_find(file_private, handles[i]);
12873e6fb72dSJason Ekstrand 		if (!syncobjs[i]) {
12883e6fb72dSJason Ekstrand 			ret = -ENOENT;
12893e6fb72dSJason Ekstrand 			goto err_put_syncobjs;
12903e6fb72dSJason Ekstrand 		}
12913e6fb72dSJason Ekstrand 	}
12923e6fb72dSJason Ekstrand 
12933e6fb72dSJason Ekstrand 	kfree(handles);
12943e6fb72dSJason Ekstrand 	*syncobjs_out = syncobjs;
12953e6fb72dSJason Ekstrand 	return 0;
12963e6fb72dSJason Ekstrand 
12973e6fb72dSJason Ekstrand err_put_syncobjs:
12983e6fb72dSJason Ekstrand 	while (i-- > 0)
12993e6fb72dSJason Ekstrand 		drm_syncobj_put(syncobjs[i]);
13003e6fb72dSJason Ekstrand 	kfree(syncobjs);
13013e6fb72dSJason Ekstrand err_free_handles:
13023e6fb72dSJason Ekstrand 	kfree(handles);
13033e6fb72dSJason Ekstrand 
13043e6fb72dSJason Ekstrand 	return ret;
13053e6fb72dSJason Ekstrand }
13063e6fb72dSJason Ekstrand 
drm_syncobj_array_free(struct drm_syncobj ** syncobjs,uint32_t count)13073e6fb72dSJason Ekstrand static void drm_syncobj_array_free(struct drm_syncobj **syncobjs,
13083e6fb72dSJason Ekstrand 				   uint32_t count)
13093e6fb72dSJason Ekstrand {
13103e6fb72dSJason Ekstrand 	uint32_t i;
1311948de842SSuraj Upadhyay 
13123e6fb72dSJason Ekstrand 	for (i = 0; i < count; i++)
13133e6fb72dSJason Ekstrand 		drm_syncobj_put(syncobjs[i]);
13143e6fb72dSJason Ekstrand 	kfree(syncobjs);
13153e6fb72dSJason Ekstrand }
13163e6fb72dSJason Ekstrand 
13175e60a10eSDave Airlie int
drm_syncobj_wait_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)13185e60a10eSDave Airlie drm_syncobj_wait_ioctl(struct drm_device *dev, void *data,
13195e60a10eSDave Airlie 		       struct drm_file *file_private)
13205e60a10eSDave Airlie {
13215e60a10eSDave Airlie 	struct drm_syncobj_wait *args = data;
1322e7aca503SJason Ekstrand 	struct drm_syncobj **syncobjs;
13238570c279SRob Clark 	unsigned int possible_flags;
13248570c279SRob Clark 	ktime_t t, *tp = NULL;
13255e60a10eSDave Airlie 	int ret = 0;
13265e60a10eSDave Airlie 
13275e60a10eSDave Airlie 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
132869fdf420SChris Wilson 		return -EOPNOTSUPP;
13295e60a10eSDave Airlie 
13308570c279SRob Clark 	possible_flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL |
13318570c279SRob Clark 			 DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
13328570c279SRob Clark 			 DRM_SYNCOBJ_WAIT_FLAGS_WAIT_DEADLINE;
13338570c279SRob Clark 
13348570c279SRob Clark 	if (args->flags & ~possible_flags)
13355e60a10eSDave Airlie 		return -EINVAL;
13365e60a10eSDave Airlie 
13375e60a10eSDave Airlie 	if (args->count_handles == 0)
13388570c279SRob Clark 		return 0;
13395e60a10eSDave Airlie 
13403e6fb72dSJason Ekstrand 	ret = drm_syncobj_array_find(file_private,
13415e60a10eSDave Airlie 				     u64_to_user_ptr(args->handles),
13423e6fb72dSJason Ekstrand 				     args->count_handles,
13433e6fb72dSJason Ekstrand 				     &syncobjs);
13443e6fb72dSJason Ekstrand 	if (ret < 0)
13453e6fb72dSJason Ekstrand 		return ret;
13465e60a10eSDave Airlie 
13478570c279SRob Clark 	if (args->flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_DEADLINE) {
13488570c279SRob Clark 		t = ns_to_ktime(args->deadline_nsec);
13498570c279SRob Clark 		tp = &t;
13508570c279SRob Clark 	}
13518570c279SRob Clark 
1352e7aca503SJason Ekstrand 	ret = drm_syncobj_array_wait(dev, file_private,
13538570c279SRob Clark 				     args, NULL, syncobjs, false, tp);
13545e60a10eSDave Airlie 
13553e6fb72dSJason Ekstrand 	drm_syncobj_array_free(syncobjs, args->count_handles);
13565e60a10eSDave Airlie 
13575e60a10eSDave Airlie 	return ret;
13585e60a10eSDave Airlie }
1359aa4035d2SJason Ekstrand 
1360aa4035d2SJason Ekstrand int
drm_syncobj_timeline_wait_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)136101d6c357SChunming Zhou drm_syncobj_timeline_wait_ioctl(struct drm_device *dev, void *data,
136201d6c357SChunming Zhou 				struct drm_file *file_private)
136301d6c357SChunming Zhou {
136401d6c357SChunming Zhou 	struct drm_syncobj_timeline_wait *args = data;
136501d6c357SChunming Zhou 	struct drm_syncobj **syncobjs;
13668570c279SRob Clark 	unsigned int possible_flags;
13678570c279SRob Clark 	ktime_t t, *tp = NULL;
136801d6c357SChunming Zhou 	int ret = 0;
136901d6c357SChunming Zhou 
1370060cebb2SLionel Landwerlin 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
13715ec77638SLionel Landwerlin 		return -EOPNOTSUPP;
137201d6c357SChunming Zhou 
13738570c279SRob Clark 	possible_flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL |
137401d6c357SChunming Zhou 			 DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
13758570c279SRob Clark 			 DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE |
13768570c279SRob Clark 			 DRM_SYNCOBJ_WAIT_FLAGS_WAIT_DEADLINE;
13778570c279SRob Clark 
13788570c279SRob Clark 	if (args->flags & ~possible_flags)
137901d6c357SChunming Zhou 		return -EINVAL;
138001d6c357SChunming Zhou 
138101d6c357SChunming Zhou 	if (args->count_handles == 0)
13828570c279SRob Clark 		return 0;
138301d6c357SChunming Zhou 
138401d6c357SChunming Zhou 	ret = drm_syncobj_array_find(file_private,
138501d6c357SChunming Zhou 				     u64_to_user_ptr(args->handles),
138601d6c357SChunming Zhou 				     args->count_handles,
138701d6c357SChunming Zhou 				     &syncobjs);
138801d6c357SChunming Zhou 	if (ret < 0)
138901d6c357SChunming Zhou 		return ret;
139001d6c357SChunming Zhou 
13918570c279SRob Clark 	if (args->flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_DEADLINE) {
13928570c279SRob Clark 		t = ns_to_ktime(args->deadline_nsec);
13938570c279SRob Clark 		tp = &t;
13948570c279SRob Clark 	}
13958570c279SRob Clark 
139601d6c357SChunming Zhou 	ret = drm_syncobj_array_wait(dev, file_private,
13978570c279SRob Clark 				     NULL, args, syncobjs, true, tp);
139801d6c357SChunming Zhou 
139901d6c357SChunming Zhou 	drm_syncobj_array_free(syncobjs, args->count_handles);
140001d6c357SChunming Zhou 
140101d6c357SChunming Zhou 	return ret;
140201d6c357SChunming Zhou }
140301d6c357SChunming Zhou 
syncobj_eventfd_entry_fence_func(struct dma_fence * fence,struct dma_fence_cb * cb)1404c7a47229SSimon Ser static void syncobj_eventfd_entry_fence_func(struct dma_fence *fence,
1405c7a47229SSimon Ser 					     struct dma_fence_cb *cb)
1406c7a47229SSimon Ser {
1407c7a47229SSimon Ser 	struct syncobj_eventfd_entry *entry =
1408c7a47229SSimon Ser 		container_of(cb, struct syncobj_eventfd_entry, fence_cb);
1409c7a47229SSimon Ser 
14103652117fSChristian Brauner 	eventfd_signal(entry->ev_fd_ctx);
1411c7a47229SSimon Ser 	syncobj_eventfd_entry_free(entry);
1412c7a47229SSimon Ser }
1413c7a47229SSimon Ser 
1414c7a47229SSimon Ser static void
syncobj_eventfd_entry_func(struct drm_syncobj * syncobj,struct syncobj_eventfd_entry * entry)1415c7a47229SSimon Ser syncobj_eventfd_entry_func(struct drm_syncobj *syncobj,
1416c7a47229SSimon Ser 			   struct syncobj_eventfd_entry *entry)
1417c7a47229SSimon Ser {
1418c7a47229SSimon Ser 	int ret;
1419c7a47229SSimon Ser 	struct dma_fence *fence;
1420c7a47229SSimon Ser 
1421c7a47229SSimon Ser 	/* This happens inside the syncobj lock */
1422c7a47229SSimon Ser 	fence = dma_fence_get(rcu_dereference_protected(syncobj->fence, 1));
14232aa6f5b0SErik Kurzinger 	if (!fence)
14242aa6f5b0SErik Kurzinger 		return;
14252aa6f5b0SErik Kurzinger 
1426c7a47229SSimon Ser 	ret = dma_fence_chain_find_seqno(&fence, entry->point);
14272aa6f5b0SErik Kurzinger 	if (ret != 0) {
14282aa6f5b0SErik Kurzinger 		/* The given seqno has not been submitted yet. */
1429c7a47229SSimon Ser 		dma_fence_put(fence);
1430c7a47229SSimon Ser 		return;
14312aa6f5b0SErik Kurzinger 	} else if (!fence) {
14322aa6f5b0SErik Kurzinger 		/* If dma_fence_chain_find_seqno returns 0 but sets the fence
14332aa6f5b0SErik Kurzinger 		 * to NULL, it implies that the given seqno is signaled and a
14342aa6f5b0SErik Kurzinger 		 * later seqno has already been submitted. Assign a stub fence
14352aa6f5b0SErik Kurzinger 		 * so that the eventfd still gets signaled below.
14362aa6f5b0SErik Kurzinger 		 */
14372aa6f5b0SErik Kurzinger 		fence = dma_fence_get_stub();
1438c7a47229SSimon Ser 	}
1439c7a47229SSimon Ser 
1440c7a47229SSimon Ser 	list_del_init(&entry->node);
1441c7a47229SSimon Ser 	entry->fence = fence;
1442c7a47229SSimon Ser 
1443c7a47229SSimon Ser 	if (entry->flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE) {
14443652117fSChristian Brauner 		eventfd_signal(entry->ev_fd_ctx);
1445c7a47229SSimon Ser 		syncobj_eventfd_entry_free(entry);
1446c7a47229SSimon Ser 	} else {
1447c7a47229SSimon Ser 		ret = dma_fence_add_callback(fence, &entry->fence_cb,
1448c7a47229SSimon Ser 					     syncobj_eventfd_entry_fence_func);
1449c7a47229SSimon Ser 		if (ret == -ENOENT) {
14503652117fSChristian Brauner 			eventfd_signal(entry->ev_fd_ctx);
1451c7a47229SSimon Ser 			syncobj_eventfd_entry_free(entry);
1452c7a47229SSimon Ser 		}
1453c7a47229SSimon Ser 	}
1454c7a47229SSimon Ser }
1455c7a47229SSimon Ser 
1456c7a47229SSimon Ser int
drm_syncobj_eventfd_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)1457c7a47229SSimon Ser drm_syncobj_eventfd_ioctl(struct drm_device *dev, void *data,
1458c7a47229SSimon Ser 			  struct drm_file *file_private)
1459c7a47229SSimon Ser {
1460c7a47229SSimon Ser 	struct drm_syncobj_eventfd *args = data;
1461c7a47229SSimon Ser 	struct drm_syncobj *syncobj;
1462c7a47229SSimon Ser 	struct eventfd_ctx *ev_fd_ctx;
1463c7a47229SSimon Ser 	struct syncobj_eventfd_entry *entry;
14648c7c44beST.J. Mercier 	int ret;
1465c7a47229SSimon Ser 
1466c7a47229SSimon Ser 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
1467c7a47229SSimon Ser 		return -EOPNOTSUPP;
1468c7a47229SSimon Ser 
1469c7a47229SSimon Ser 	if (args->flags & ~DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE)
1470c7a47229SSimon Ser 		return -EINVAL;
1471c7a47229SSimon Ser 
1472c7a47229SSimon Ser 	if (args->pad)
1473c7a47229SSimon Ser 		return -EINVAL;
1474c7a47229SSimon Ser 
1475c7a47229SSimon Ser 	syncobj = drm_syncobj_find(file_private, args->handle);
1476c7a47229SSimon Ser 	if (!syncobj)
1477c7a47229SSimon Ser 		return -ENOENT;
1478c7a47229SSimon Ser 
1479c7a47229SSimon Ser 	ev_fd_ctx = eventfd_ctx_fdget(args->fd);
14808c7c44beST.J. Mercier 	if (IS_ERR(ev_fd_ctx)) {
14818c7c44beST.J. Mercier 		ret = PTR_ERR(ev_fd_ctx);
14828c7c44beST.J. Mercier 		goto err_fdget;
14838c7c44beST.J. Mercier 	}
1484c7a47229SSimon Ser 
1485c7a47229SSimon Ser 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
1486c7a47229SSimon Ser 	if (!entry) {
14878c7c44beST.J. Mercier 		ret = -ENOMEM;
14888c7c44beST.J. Mercier 		goto err_kzalloc;
1489c7a47229SSimon Ser 	}
1490c7a47229SSimon Ser 	entry->syncobj = syncobj;
1491c7a47229SSimon Ser 	entry->ev_fd_ctx = ev_fd_ctx;
1492c7a47229SSimon Ser 	entry->point = args->point;
1493c7a47229SSimon Ser 	entry->flags = args->flags;
1494c7a47229SSimon Ser 
1495c7a47229SSimon Ser 	drm_syncobj_add_eventfd(syncobj, entry);
1496c7a47229SSimon Ser 	drm_syncobj_put(syncobj);
1497c7a47229SSimon Ser 
1498c7a47229SSimon Ser 	return 0;
14998c7c44beST.J. Mercier 
15008c7c44beST.J. Mercier err_kzalloc:
15018c7c44beST.J. Mercier 	eventfd_ctx_put(ev_fd_ctx);
15028c7c44beST.J. Mercier err_fdget:
15038c7c44beST.J. Mercier 	drm_syncobj_put(syncobj);
15048c7c44beST.J. Mercier 	return ret;
1505c7a47229SSimon Ser }
150601d6c357SChunming Zhou 
150701d6c357SChunming Zhou int
drm_syncobj_reset_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)1508aa4035d2SJason Ekstrand drm_syncobj_reset_ioctl(struct drm_device *dev, void *data,
1509aa4035d2SJason Ekstrand 			struct drm_file *file_private)
1510aa4035d2SJason Ekstrand {
1511aa4035d2SJason Ekstrand 	struct drm_syncobj_array *args = data;
1512aa4035d2SJason Ekstrand 	struct drm_syncobj **syncobjs;
1513aa4035d2SJason Ekstrand 	uint32_t i;
1514aa4035d2SJason Ekstrand 	int ret;
1515aa4035d2SJason Ekstrand 
1516aa4035d2SJason Ekstrand 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
151769fdf420SChris Wilson 		return -EOPNOTSUPP;
1518aa4035d2SJason Ekstrand 
1519aa4035d2SJason Ekstrand 	if (args->pad != 0)
1520aa4035d2SJason Ekstrand 		return -EINVAL;
1521aa4035d2SJason Ekstrand 
1522aa4035d2SJason Ekstrand 	if (args->count_handles == 0)
1523aa4035d2SJason Ekstrand 		return -EINVAL;
1524aa4035d2SJason Ekstrand 
1525aa4035d2SJason Ekstrand 	ret = drm_syncobj_array_find(file_private,
1526aa4035d2SJason Ekstrand 				     u64_to_user_ptr(args->handles),
1527aa4035d2SJason Ekstrand 				     args->count_handles,
1528aa4035d2SJason Ekstrand 				     &syncobjs);
1529aa4035d2SJason Ekstrand 	if (ret < 0)
1530aa4035d2SJason Ekstrand 		return ret;
1531aa4035d2SJason Ekstrand 
1532131280a1SEric Anholt 	for (i = 0; i < args->count_handles; i++)
15330b258ed1SChristian König 		drm_syncobj_replace_fence(syncobjs[i], NULL);
1534131280a1SEric Anholt 
1535aa4035d2SJason Ekstrand 	drm_syncobj_array_free(syncobjs, args->count_handles);
1536aa4035d2SJason Ekstrand 
1537131280a1SEric Anholt 	return 0;
1538aa4035d2SJason Ekstrand }
1539ffa9443fSJason Ekstrand 
1540ffa9443fSJason Ekstrand int
drm_syncobj_signal_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)1541ffa9443fSJason Ekstrand drm_syncobj_signal_ioctl(struct drm_device *dev, void *data,
1542ffa9443fSJason Ekstrand 			 struct drm_file *file_private)
1543ffa9443fSJason Ekstrand {
1544ffa9443fSJason Ekstrand 	struct drm_syncobj_array *args = data;
1545ffa9443fSJason Ekstrand 	struct drm_syncobj **syncobjs;
1546ffa9443fSJason Ekstrand 	uint32_t i;
1547ffa9443fSJason Ekstrand 	int ret;
1548ffa9443fSJason Ekstrand 
1549ffa9443fSJason Ekstrand 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
155069fdf420SChris Wilson 		return -EOPNOTSUPP;
1551ffa9443fSJason Ekstrand 
1552ffa9443fSJason Ekstrand 	if (args->pad != 0)
1553ffa9443fSJason Ekstrand 		return -EINVAL;
1554ffa9443fSJason Ekstrand 
1555ffa9443fSJason Ekstrand 	if (args->count_handles == 0)
1556ffa9443fSJason Ekstrand 		return -EINVAL;
1557ffa9443fSJason Ekstrand 
1558ffa9443fSJason Ekstrand 	ret = drm_syncobj_array_find(file_private,
1559ffa9443fSJason Ekstrand 				     u64_to_user_ptr(args->handles),
1560ffa9443fSJason Ekstrand 				     args->count_handles,
1561ffa9443fSJason Ekstrand 				     &syncobjs);
1562ffa9443fSJason Ekstrand 	if (ret < 0)
1563ffa9443fSJason Ekstrand 		return ret;
1564ffa9443fSJason Ekstrand 
1565fd921693SDavid Stevens 	for (i = 0; i < args->count_handles; i++) {
1566fd921693SDavid Stevens 		ret = drm_syncobj_assign_null_handle(syncobjs[i]);
1567fd921693SDavid Stevens 		if (ret < 0)
1568fd921693SDavid Stevens 			break;
1569fd921693SDavid Stevens 	}
1570ffa9443fSJason Ekstrand 
1571ffa9443fSJason Ekstrand 	drm_syncobj_array_free(syncobjs, args->count_handles);
1572ffa9443fSJason Ekstrand 
1573ffa9443fSJason Ekstrand 	return ret;
1574ffa9443fSJason Ekstrand }
157527b575a9SChunming Zhou 
157650d1ebefSChunming Zhou int
drm_syncobj_timeline_signal_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)157750d1ebefSChunming Zhou drm_syncobj_timeline_signal_ioctl(struct drm_device *dev, void *data,
157850d1ebefSChunming Zhou 				  struct drm_file *file_private)
157950d1ebefSChunming Zhou {
158050d1ebefSChunming Zhou 	struct drm_syncobj_timeline_array *args = data;
158150d1ebefSChunming Zhou 	struct drm_syncobj **syncobjs;
158250d1ebefSChunming Zhou 	struct dma_fence_chain **chains;
158350d1ebefSChunming Zhou 	uint64_t *points;
158450d1ebefSChunming Zhou 	uint32_t i, j;
158550d1ebefSChunming Zhou 	int ret;
158650d1ebefSChunming Zhou 
1587060cebb2SLionel Landwerlin 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
158850d1ebefSChunming Zhou 		return -EOPNOTSUPP;
158950d1ebefSChunming Zhou 
15902093dea3SChunming Zhou 	if (args->flags != 0)
159150d1ebefSChunming Zhou 		return -EINVAL;
159250d1ebefSChunming Zhou 
159350d1ebefSChunming Zhou 	if (args->count_handles == 0)
159450d1ebefSChunming Zhou 		return -EINVAL;
159550d1ebefSChunming Zhou 
159650d1ebefSChunming Zhou 	ret = drm_syncobj_array_find(file_private,
159750d1ebefSChunming Zhou 				     u64_to_user_ptr(args->handles),
159850d1ebefSChunming Zhou 				     args->count_handles,
159950d1ebefSChunming Zhou 				     &syncobjs);
160050d1ebefSChunming Zhou 	if (ret < 0)
160150d1ebefSChunming Zhou 		return ret;
160250d1ebefSChunming Zhou 
160350d1ebefSChunming Zhou 	points = kmalloc_array(args->count_handles, sizeof(*points),
160450d1ebefSChunming Zhou 			       GFP_KERNEL);
160550d1ebefSChunming Zhou 	if (!points) {
160650d1ebefSChunming Zhou 		ret = -ENOMEM;
160750d1ebefSChunming Zhou 		goto out;
160850d1ebefSChunming Zhou 	}
160950d1ebefSChunming Zhou 	if (!u64_to_user_ptr(args->points)) {
161050d1ebefSChunming Zhou 		memset(points, 0, args->count_handles * sizeof(uint64_t));
161150d1ebefSChunming Zhou 	} else if (copy_from_user(points, u64_to_user_ptr(args->points),
161250d1ebefSChunming Zhou 				  sizeof(uint64_t) * args->count_handles)) {
161350d1ebefSChunming Zhou 		ret = -EFAULT;
161450d1ebefSChunming Zhou 		goto err_points;
161550d1ebefSChunming Zhou 	}
161650d1ebefSChunming Zhou 
161750d1ebefSChunming Zhou 	chains = kmalloc_array(args->count_handles, sizeof(void *), GFP_KERNEL);
161850d1ebefSChunming Zhou 	if (!chains) {
161950d1ebefSChunming Zhou 		ret = -ENOMEM;
162050d1ebefSChunming Zhou 		goto err_points;
162150d1ebefSChunming Zhou 	}
162250d1ebefSChunming Zhou 	for (i = 0; i < args->count_handles; i++) {
1623440d0f12SChristian König 		chains[i] = dma_fence_chain_alloc();
162450d1ebefSChunming Zhou 		if (!chains[i]) {
162550d1ebefSChunming Zhou 			for (j = 0; j < i; j++)
1626440d0f12SChristian König 				dma_fence_chain_free(chains[j]);
162750d1ebefSChunming Zhou 			ret = -ENOMEM;
162850d1ebefSChunming Zhou 			goto err_chains;
162950d1ebefSChunming Zhou 		}
163050d1ebefSChunming Zhou 	}
163150d1ebefSChunming Zhou 
163250d1ebefSChunming Zhou 	for (i = 0; i < args->count_handles; i++) {
163350d1ebefSChunming Zhou 		struct dma_fence *fence = dma_fence_get_stub();
163450d1ebefSChunming Zhou 
163550d1ebefSChunming Zhou 		drm_syncobj_add_point(syncobjs[i], chains[i],
163650d1ebefSChunming Zhou 				      fence, points[i]);
163750d1ebefSChunming Zhou 		dma_fence_put(fence);
163850d1ebefSChunming Zhou 	}
163950d1ebefSChunming Zhou err_chains:
164050d1ebefSChunming Zhou 	kfree(chains);
164150d1ebefSChunming Zhou err_points:
164250d1ebefSChunming Zhou 	kfree(points);
164350d1ebefSChunming Zhou out:
164450d1ebefSChunming Zhou 	drm_syncobj_array_free(syncobjs, args->count_handles);
164550d1ebefSChunming Zhou 
164650d1ebefSChunming Zhou 	return ret;
164750d1ebefSChunming Zhou }
164850d1ebefSChunming Zhou 
drm_syncobj_query_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)164927b575a9SChunming Zhou int drm_syncobj_query_ioctl(struct drm_device *dev, void *data,
165027b575a9SChunming Zhou 			    struct drm_file *file_private)
165127b575a9SChunming Zhou {
165227b575a9SChunming Zhou 	struct drm_syncobj_timeline_array *args = data;
165327b575a9SChunming Zhou 	struct drm_syncobj **syncobjs;
165427b575a9SChunming Zhou 	uint64_t __user *points = u64_to_user_ptr(args->points);
165527b575a9SChunming Zhou 	uint32_t i;
165627b575a9SChunming Zhou 	int ret;
165727b575a9SChunming Zhou 
1658060cebb2SLionel Landwerlin 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
1659060cebb2SLionel Landwerlin 		return -EOPNOTSUPP;
166027b575a9SChunming Zhou 
16612093dea3SChunming Zhou 	if (args->flags & ~DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED)
166227b575a9SChunming Zhou 		return -EINVAL;
166327b575a9SChunming Zhou 
166427b575a9SChunming Zhou 	if (args->count_handles == 0)
166527b575a9SChunming Zhou 		return -EINVAL;
166627b575a9SChunming Zhou 
166727b575a9SChunming Zhou 	ret = drm_syncobj_array_find(file_private,
166827b575a9SChunming Zhou 				     u64_to_user_ptr(args->handles),
166927b575a9SChunming Zhou 				     args->count_handles,
167027b575a9SChunming Zhou 				     &syncobjs);
167127b575a9SChunming Zhou 	if (ret < 0)
167227b575a9SChunming Zhou 		return ret;
167327b575a9SChunming Zhou 
167427b575a9SChunming Zhou 	for (i = 0; i < args->count_handles; i++) {
167527b575a9SChunming Zhou 		struct dma_fence_chain *chain;
167627b575a9SChunming Zhou 		struct dma_fence *fence;
167727b575a9SChunming Zhou 		uint64_t point;
167827b575a9SChunming Zhou 
167927b575a9SChunming Zhou 		fence = drm_syncobj_fence_get(syncobjs[i]);
168027b575a9SChunming Zhou 		chain = to_dma_fence_chain(fence);
168127b575a9SChunming Zhou 		if (chain) {
16822093dea3SChunming Zhou 			struct dma_fence *iter, *last_signaled =
16832093dea3SChunming Zhou 				dma_fence_get(fence);
168427b575a9SChunming Zhou 
16852093dea3SChunming Zhou 			if (args->flags &
16862093dea3SChunming Zhou 			    DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED) {
16872093dea3SChunming Zhou 				point = fence->seqno;
16882093dea3SChunming Zhou 			} else {
168927b575a9SChunming Zhou 				dma_fence_chain_for_each(iter, fence) {
1690b33b556cSChristian König 					if (iter->context != fence->context) {
1691b33b556cSChristian König 						dma_fence_put(iter);
169227b575a9SChunming Zhou 						/* It is most likely that timeline has
169327b575a9SChunming Zhou 						* unorder points. */
169427b575a9SChunming Zhou 						break;
169527b575a9SChunming Zhou 					}
1696b33b556cSChristian König 					dma_fence_put(last_signaled);
1697b33b556cSChristian König 					last_signaled = dma_fence_get(iter);
1698b33b556cSChristian König 				}
169927b575a9SChunming Zhou 				point = dma_fence_is_signaled(last_signaled) ?
170027b575a9SChunming Zhou 					last_signaled->seqno :
170127b575a9SChunming Zhou 					to_dma_fence_chain(last_signaled)->prev_seqno;
17022093dea3SChunming Zhou 			}
170327b575a9SChunming Zhou 			dma_fence_put(last_signaled);
170427b575a9SChunming Zhou 		} else {
170527b575a9SChunming Zhou 			point = 0;
170627b575a9SChunming Zhou 		}
17072093dea3SChunming Zhou 		dma_fence_put(fence);
170827b575a9SChunming Zhou 		ret = copy_to_user(&points[i], &point, sizeof(uint64_t));
170927b575a9SChunming Zhou 		ret = ret ? -EFAULT : 0;
171027b575a9SChunming Zhou 		if (ret)
171127b575a9SChunming Zhou 			break;
171227b575a9SChunming Zhou 	}
171327b575a9SChunming Zhou 	drm_syncobj_array_free(syncobjs, args->count_handles);
171427b575a9SChunming Zhou 
171527b575a9SChunming Zhou 	return ret;
171627b575a9SChunming Zhou }
1717