xref: /openbmc/linux/drivers/gpu/drm/drm_syncobj.c (revision 20e1e1a2)
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  *
12977d1a6dbSLionel Landwerlin  * Similarly, &DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT takes an array of syncobj
13077d1a6dbSLionel Landwerlin  * handles as well as an array of u64 points and does a host-side wait on all
13177d1a6dbSLionel Landwerlin  * of syncobj fences at the given points simultaneously.
13277d1a6dbSLionel Landwerlin  *
13377d1a6dbSLionel Landwerlin  * &DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT also adds the ability to wait for a given
13477d1a6dbSLionel Landwerlin  * fence to materialize on the timeline without waiting for the fence to be
13577d1a6dbSLionel Landwerlin  * signaled by using the &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE flag. This
13677d1a6dbSLionel Landwerlin  * requirement is inherited from the wait-before-signal behavior required by
13777d1a6dbSLionel Landwerlin  * the Vulkan timeline semaphore API.
13877d1a6dbSLionel Landwerlin  *
139c7a47229SSimon Ser  * Alternatively, &DRM_IOCTL_SYNCOBJ_EVENTFD can be used to wait without
140c7a47229SSimon Ser  * blocking: an eventfd will be signaled when the syncobj is. This is useful to
141c7a47229SSimon Ser  * integrate the wait in an event loop.
142c7a47229SSimon Ser  *
143f246ff5cSJason Ekstrand  *
144f246ff5cSJason Ekstrand  * Import/export of syncobjs
145f246ff5cSJason Ekstrand  * -------------------------
146f246ff5cSJason Ekstrand  *
147f246ff5cSJason Ekstrand  * &DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE and &DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD
148f246ff5cSJason Ekstrand  * provide two mechanisms for import/export of syncobjs.
149f246ff5cSJason Ekstrand  *
150f246ff5cSJason Ekstrand  * The first lets the client import or export an entire syncobj to a file
151f246ff5cSJason Ekstrand  * descriptor.
152f246ff5cSJason Ekstrand  * These fd's are opaque and have no other use case, except passing the
153f246ff5cSJason Ekstrand  * syncobj between processes.
154f246ff5cSJason Ekstrand  * All exported file descriptors and any syncobj handles created as a
155f246ff5cSJason Ekstrand  * result of importing those file descriptors own a reference to the
156f246ff5cSJason Ekstrand  * same underlying struct &drm_syncobj and the syncobj can be used
157f246ff5cSJason Ekstrand  * persistently across all the processes with which it is shared.
158f246ff5cSJason Ekstrand  * The syncobj is freed only once the last reference is dropped.
159f246ff5cSJason Ekstrand  * Unlike dma-buf, importing a syncobj creates a new handle (with its own
160f246ff5cSJason Ekstrand  * reference) for every import instead of de-duplicating.
161f246ff5cSJason Ekstrand  * The primary use-case of this persistent import/export is for shared
162f246ff5cSJason Ekstrand  * Vulkan fences and semaphores.
163f246ff5cSJason Ekstrand  *
164f246ff5cSJason Ekstrand  * The second import/export mechanism, which is indicated by
165f246ff5cSJason Ekstrand  * &DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE or
166f246ff5cSJason Ekstrand  * &DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE lets the client
167f246ff5cSJason Ekstrand  * import/export the syncobj's current fence from/to a &sync_file.
168f246ff5cSJason Ekstrand  * When a syncobj is exported to a sync file, that sync file wraps the
169f246ff5cSJason Ekstrand  * sycnobj's fence at the time of export and any later signal or reset
170f246ff5cSJason Ekstrand  * operations on the syncobj will not affect the exported sync file.
171f246ff5cSJason Ekstrand  * When a sync file is imported into a syncobj, the syncobj's fence is set
172f246ff5cSJason Ekstrand  * to the fence wrapped by that sync file.
173f246ff5cSJason Ekstrand  * Because sync files are immutable, resetting or signaling the syncobj
174f246ff5cSJason Ekstrand  * will not affect any sync files whose fences have been imported into the
175f246ff5cSJason Ekstrand  * syncobj.
17677d1a6dbSLionel Landwerlin  *
17777d1a6dbSLionel Landwerlin  *
17877d1a6dbSLionel Landwerlin  * Import/export of timeline points in timeline syncobjs
17977d1a6dbSLionel Landwerlin  * -----------------------------------------------------
18077d1a6dbSLionel Landwerlin  *
18177d1a6dbSLionel Landwerlin  * &DRM_IOCTL_SYNCOBJ_TRANSFER provides a mechanism to transfer a struct
18277d1a6dbSLionel Landwerlin  * &dma_fence_chain of a syncobj at a given u64 point to another u64 point
18377d1a6dbSLionel Landwerlin  * into another syncobj.
18477d1a6dbSLionel Landwerlin  *
18577d1a6dbSLionel Landwerlin  * Note that if you want to transfer a struct &dma_fence_chain from a given
18677d1a6dbSLionel Landwerlin  * point on a timeline syncobj from/into a binary syncobj, you can use the
18777d1a6dbSLionel Landwerlin  * point 0 to mean take/replace the fence in the syncobj.
188e9083420SDave Airlie  */
189e9083420SDave Airlie 
1900500c04eSSam Ravnborg #include <linux/anon_inodes.h>
191ec8d985fSChristian König #include <linux/dma-fence-unwrap.h>
192c7a47229SSimon Ser #include <linux/eventfd.h>
193e9083420SDave Airlie #include <linux/file.h>
194e9083420SDave Airlie #include <linux/fs.h>
195e7aca503SJason Ekstrand #include <linux/sched/signal.h>
1960500c04eSSam Ravnborg #include <linux/sync_file.h>
1970500c04eSSam Ravnborg #include <linux/uaccess.h>
1980500c04eSSam Ravnborg 
199d89281c5SSam Ravnborg #include <drm/drm.h>
2000500c04eSSam Ravnborg #include <drm/drm_drv.h>
2010500c04eSSam Ravnborg #include <drm/drm_file.h>
2020500c04eSSam Ravnborg #include <drm/drm_gem.h>
2030500c04eSSam Ravnborg #include <drm/drm_print.h>
2040500c04eSSam Ravnborg #include <drm/drm_syncobj.h>
205b9436986SVille Syrjälä #include <drm/drm_utils.h>
206e9083420SDave Airlie 
207e9083420SDave Airlie #include "drm_internal.h"
208e9083420SDave Airlie 
20961a98b1bSChristian König struct syncobj_wait_entry {
21061a98b1bSChristian König 	struct list_head node;
21161a98b1bSChristian König 	struct task_struct *task;
21261a98b1bSChristian König 	struct dma_fence *fence;
21361a98b1bSChristian König 	struct dma_fence_cb fence_cb;
21401d6c357SChunming Zhou 	u64    point;
21561a98b1bSChristian König };
21661a98b1bSChristian König 
21761a98b1bSChristian König static void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj,
21861a98b1bSChristian König 				      struct syncobj_wait_entry *wait);
21961a98b1bSChristian König 
220c7a47229SSimon Ser struct syncobj_eventfd_entry {
221c7a47229SSimon Ser 	struct list_head node;
222c7a47229SSimon Ser 	struct dma_fence *fence;
223c7a47229SSimon Ser 	struct dma_fence_cb fence_cb;
224c7a47229SSimon Ser 	struct drm_syncobj *syncobj;
225c7a47229SSimon Ser 	struct eventfd_ctx *ev_fd_ctx;
226c7a47229SSimon Ser 	u64 point;
227c7a47229SSimon Ser 	u32 flags;
228c7a47229SSimon Ser };
229c7a47229SSimon Ser 
230c7a47229SSimon Ser static void
231c7a47229SSimon Ser syncobj_eventfd_entry_func(struct drm_syncobj *syncobj,
232c7a47229SSimon Ser 			   struct syncobj_eventfd_entry *entry);
233c7a47229SSimon Ser 
234e9083420SDave Airlie /**
235e9083420SDave Airlie  * drm_syncobj_find - lookup and reference a sync object.
236e9083420SDave Airlie  * @file_private: drm file private pointer
237e9083420SDave Airlie  * @handle: sync object handle to lookup.
238e9083420SDave Airlie  *
239924fe8dfSDaniel Vetter  * Returns a reference to the syncobj pointed to by handle or NULL. The
240924fe8dfSDaniel Vetter  * reference must be released by calling drm_syncobj_put().
241e9083420SDave Airlie  */
drm_syncobj_find(struct drm_file * file_private,u32 handle)242e9083420SDave Airlie struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private,
243e9083420SDave Airlie 				     u32 handle)
244e9083420SDave Airlie {
245e9083420SDave Airlie 	struct drm_syncobj *syncobj;
246e9083420SDave Airlie 
247e9083420SDave Airlie 	spin_lock(&file_private->syncobj_table_lock);
248e9083420SDave Airlie 
249e9083420SDave Airlie 	/* Check if we currently have a reference on the object */
250e9083420SDave Airlie 	syncobj = idr_find(&file_private->syncobj_idr, handle);
251e9083420SDave Airlie 	if (syncobj)
252e9083420SDave Airlie 		drm_syncobj_get(syncobj);
253e9083420SDave Airlie 
254e9083420SDave Airlie 	spin_unlock(&file_private->syncobj_table_lock);
255e9083420SDave Airlie 
256e9083420SDave Airlie 	return syncobj;
257e9083420SDave Airlie }
258e9083420SDave Airlie EXPORT_SYMBOL(drm_syncobj_find);
259e9083420SDave Airlie 
drm_syncobj_fence_add_wait(struct drm_syncobj * syncobj,struct syncobj_wait_entry * wait)26061a98b1bSChristian König static void drm_syncobj_fence_add_wait(struct drm_syncobj *syncobj,
26161a98b1bSChristian König 				       struct syncobj_wait_entry *wait)
26243cf1fc0SChunming Zhou {
26301d6c357SChunming Zhou 	struct dma_fence *fence;
26401d6c357SChunming Zhou 
26561a98b1bSChristian König 	if (wait->fence)
26661a98b1bSChristian König 		return;
267337fe9f5SJason Ekstrand 
268131280a1SEric Anholt 	spin_lock(&syncobj->lock);
269131280a1SEric Anholt 	/* We've already tried once to get a fence and failed.  Now that we
270131280a1SEric Anholt 	 * have the lock, try one more time just to be sure we don't add a
271131280a1SEric Anholt 	 * callback when a fence has already been set.
272131280a1SEric Anholt 	 */
27301d6c357SChunming Zhou 	fence = dma_fence_get(rcu_dereference_protected(syncobj->fence, 1));
27401d6c357SChunming Zhou 	if (!fence || dma_fence_chain_find_seqno(&fence, wait->point)) {
27501d6c357SChunming Zhou 		dma_fence_put(fence);
27661a98b1bSChristian König 		list_add_tail(&wait->node, &syncobj->cb_list);
27701d6c357SChunming Zhou 	} else if (!fence) {
27801d6c357SChunming Zhou 		wait->fence = dma_fence_get_stub();
27901d6c357SChunming Zhou 	} else {
28001d6c357SChunming Zhou 		wait->fence = fence;
28101d6c357SChunming Zhou 	}
282131280a1SEric Anholt 	spin_unlock(&syncobj->lock);
28348197bc5SChunming Zhou }
28448197bc5SChunming Zhou 
drm_syncobj_remove_wait(struct drm_syncobj * syncobj,struct syncobj_wait_entry * wait)28561a98b1bSChristian König static void drm_syncobj_remove_wait(struct drm_syncobj *syncobj,
28661a98b1bSChristian König 				    struct syncobj_wait_entry *wait)
287131280a1SEric Anholt {
28861a98b1bSChristian König 	if (!wait->node.next)
28961a98b1bSChristian König 		return;
29061a98b1bSChristian König 
291131280a1SEric Anholt 	spin_lock(&syncobj->lock);
29261a98b1bSChristian König 	list_del_init(&wait->node);
293131280a1SEric Anholt 	spin_unlock(&syncobj->lock);
29448197bc5SChunming Zhou }
295131280a1SEric Anholt 
296c7a47229SSimon Ser static void
syncobj_eventfd_entry_free(struct syncobj_eventfd_entry * entry)297c7a47229SSimon Ser syncobj_eventfd_entry_free(struct syncobj_eventfd_entry *entry)
298c7a47229SSimon Ser {
299c7a47229SSimon Ser 	eventfd_ctx_put(entry->ev_fd_ctx);
300c7a47229SSimon Ser 	dma_fence_put(entry->fence);
301c7a47229SSimon Ser 	/* This happens either inside the syncobj lock, or after the node has
302c7a47229SSimon Ser 	 * already been removed from the list.
303c7a47229SSimon Ser 	 */
304c7a47229SSimon Ser 	list_del(&entry->node);
305c7a47229SSimon Ser 	kfree(entry);
306c7a47229SSimon Ser }
307c7a47229SSimon Ser 
308c7a47229SSimon Ser static void
drm_syncobj_add_eventfd(struct drm_syncobj * syncobj,struct syncobj_eventfd_entry * entry)309c7a47229SSimon Ser drm_syncobj_add_eventfd(struct drm_syncobj *syncobj,
310c7a47229SSimon Ser 			struct syncobj_eventfd_entry *entry)
311c7a47229SSimon Ser {
312c7a47229SSimon Ser 	spin_lock(&syncobj->lock);
313c7a47229SSimon Ser 	list_add_tail(&entry->node, &syncobj->ev_fd_list);
314c7a47229SSimon Ser 	syncobj_eventfd_entry_func(syncobj, entry);
315c7a47229SSimon Ser 	spin_unlock(&syncobj->lock);
316c7a47229SSimon Ser }
317c7a47229SSimon Ser 
318e9083420SDave Airlie /**
31944f8a139SChristian König  * drm_syncobj_add_point - add new timeline point to the syncobj
32044f8a139SChristian König  * @syncobj: sync object to add timeline point do
32144f8a139SChristian König  * @chain: chain node to use to add the point
32244f8a139SChristian König  * @fence: fence to encapsulate in the chain node
32344f8a139SChristian König  * @point: sequence number to use for the point
32444f8a139SChristian König  *
32544f8a139SChristian König  * Add the chain node as new timeline point to the syncobj.
32644f8a139SChristian König  */
drm_syncobj_add_point(struct drm_syncobj * syncobj,struct dma_fence_chain * chain,struct dma_fence * fence,uint64_t point)32744f8a139SChristian König void drm_syncobj_add_point(struct drm_syncobj *syncobj,
32844f8a139SChristian König 			   struct dma_fence_chain *chain,
32944f8a139SChristian König 			   struct dma_fence *fence,
33044f8a139SChristian König 			   uint64_t point)
33144f8a139SChristian König {
332c7a47229SSimon Ser 	struct syncobj_wait_entry *wait_cur, *wait_tmp;
333c7a47229SSimon Ser 	struct syncobj_eventfd_entry *ev_fd_cur, *ev_fd_tmp;
33444f8a139SChristian König 	struct dma_fence *prev;
33544f8a139SChristian König 
33644f8a139SChristian König 	dma_fence_get(fence);
33744f8a139SChristian König 
33844f8a139SChristian König 	spin_lock(&syncobj->lock);
33944f8a139SChristian König 
34044f8a139SChristian König 	prev = drm_syncobj_fence_get(syncobj);
34144f8a139SChristian König 	/* You are adding an unorder point to timeline, which could cause payload returned from query_ioctl is 0! */
34244f8a139SChristian König 	if (prev && prev->seqno >= point)
34370eca5d5SDaniel Vetter 		DRM_DEBUG("You are adding an unorder point to timeline!\n");
34444f8a139SChristian König 	dma_fence_chain_init(chain, prev, fence, point);
34544f8a139SChristian König 	rcu_assign_pointer(syncobj->fence, &chain->base);
34644f8a139SChristian König 
347c7a47229SSimon Ser 	list_for_each_entry_safe(wait_cur, wait_tmp, &syncobj->cb_list, node)
348c7a47229SSimon Ser 		syncobj_wait_syncobj_func(syncobj, wait_cur);
349c7a47229SSimon Ser 	list_for_each_entry_safe(ev_fd_cur, ev_fd_tmp, &syncobj->ev_fd_list, node)
350c7a47229SSimon Ser 		syncobj_eventfd_entry_func(syncobj, ev_fd_cur);
35144f8a139SChristian König 	spin_unlock(&syncobj->lock);
35244f8a139SChristian König 
35344f8a139SChristian König 	/* Walk the chain once to trigger garbage collection */
35444f8a139SChristian König 	dma_fence_chain_for_each(fence, prev);
35544f8a139SChristian König 	dma_fence_put(prev);
35644f8a139SChristian König }
35744f8a139SChristian König EXPORT_SYMBOL(drm_syncobj_add_point);
35844f8a139SChristian König 
35944f8a139SChristian König /**
360e9083420SDave Airlie  * drm_syncobj_replace_fence - replace fence in a sync object.
361e9083420SDave Airlie  * @syncobj: Sync object to replace fence in
362e9083420SDave Airlie  * @fence: fence to install in sync file.
363e9083420SDave Airlie  *
3640b258ed1SChristian König  * This replaces the fence on a sync object.
365e9083420SDave Airlie  */
drm_syncobj_replace_fence(struct drm_syncobj * syncobj,struct dma_fence * fence)36600fc2c26SChris Wilson void drm_syncobj_replace_fence(struct drm_syncobj *syncobj,
367e9083420SDave Airlie 			       struct dma_fence *fence)
368e9083420SDave Airlie {
369131280a1SEric Anholt 	struct dma_fence *old_fence;
370c7a47229SSimon Ser 	struct syncobj_wait_entry *wait_cur, *wait_tmp;
371c7a47229SSimon Ser 	struct syncobj_eventfd_entry *ev_fd_cur, *ev_fd_tmp;
372e9083420SDave Airlie 
373131280a1SEric Anholt 	if (fence)
374131280a1SEric Anholt 		dma_fence_get(fence);
375131280a1SEric Anholt 
376131280a1SEric Anholt 	spin_lock(&syncobj->lock);
377131280a1SEric Anholt 
378131280a1SEric Anholt 	old_fence = rcu_dereference_protected(syncobj->fence,
379131280a1SEric Anholt 					      lockdep_is_held(&syncobj->lock));
380131280a1SEric Anholt 	rcu_assign_pointer(syncobj->fence, fence);
381131280a1SEric Anholt 
382131280a1SEric Anholt 	if (fence != old_fence) {
383c7a47229SSimon Ser 		list_for_each_entry_safe(wait_cur, wait_tmp, &syncobj->cb_list, node)
384c7a47229SSimon Ser 			syncobj_wait_syncobj_func(syncobj, wait_cur);
385c7a47229SSimon Ser 		list_for_each_entry_safe(ev_fd_cur, ev_fd_tmp, &syncobj->ev_fd_list, node)
386c7a47229SSimon Ser 			syncobj_eventfd_entry_func(syncobj, ev_fd_cur);
3879c19fb10SJason Ekstrand 	}
388131280a1SEric Anholt 
389131280a1SEric Anholt 	spin_unlock(&syncobj->lock);
390131280a1SEric Anholt 
391131280a1SEric Anholt 	dma_fence_put(old_fence);
392e9083420SDave Airlie }
393e9083420SDave Airlie EXPORT_SYMBOL(drm_syncobj_replace_fence);
394e9083420SDave Airlie 
39586bbd89dSChristian König /**
39686bbd89dSChristian König  * drm_syncobj_assign_null_handle - assign a stub fence to the sync object
39786bbd89dSChristian König  * @syncobj: sync object to assign the fence on
39886bbd89dSChristian König  *
39986bbd89dSChristian König  * Assign a already signaled stub fence to the sync object.
40086bbd89dSChristian König  */
drm_syncobj_assign_null_handle(struct drm_syncobj * syncobj)401fd921693SDavid Stevens static int drm_syncobj_assign_null_handle(struct drm_syncobj *syncobj)
4021fc08218SJason Ekstrand {
403f781f661SChristian König 	struct dma_fence *fence = dma_fence_allocate_private_stub(ktime_get());
404fd921693SDavid Stevens 
40500ae1491SDan Carpenter 	if (!fence)
40600ae1491SDan Carpenter 		return -ENOMEM;
4071fc08218SJason Ekstrand 
4080b258ed1SChristian König 	drm_syncobj_replace_fence(syncobj, fence);
40986bbd89dSChristian König 	dma_fence_put(fence);
410fd921693SDavid Stevens 	return 0;
4111fc08218SJason Ekstrand }
4121fc08218SJason Ekstrand 
413bc9c80feSChristian König /* 5s default for wait submission */
414bc9c80feSChristian König #define DRM_SYNCOBJ_WAIT_FOR_SUBMIT_TIMEOUT 5000000000ULL
415924fe8dfSDaniel Vetter /**
416924fe8dfSDaniel Vetter  * drm_syncobj_find_fence - lookup and reference the fence in a sync object
417924fe8dfSDaniel Vetter  * @file_private: drm file private pointer
418924fe8dfSDaniel Vetter  * @handle: sync object handle to lookup.
4190a6730eaSChunming Zhou  * @point: timeline point
420871edc96SChunming Zhou  * @flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or not
421924fe8dfSDaniel Vetter  * @fence: out parameter for the fence
422924fe8dfSDaniel Vetter  *
423924fe8dfSDaniel Vetter  * This is just a convenience function that combines drm_syncobj_find() and
424131280a1SEric Anholt  * drm_syncobj_fence_get().
425924fe8dfSDaniel Vetter  *
426924fe8dfSDaniel Vetter  * Returns 0 on success or a negative error value on failure. On success @fence
427924fe8dfSDaniel Vetter  * contains a reference to the fence, which must be released by calling
428924fe8dfSDaniel Vetter  * dma_fence_put().
429924fe8dfSDaniel Vetter  */
drm_syncobj_find_fence(struct drm_file * file_private,u32 handle,u64 point,u64 flags,struct dma_fence ** fence)430afaf5923SJason Ekstrand int drm_syncobj_find_fence(struct drm_file *file_private,
431649fdce2SChunming Zhou 			   u32 handle, u64 point, u64 flags,
432e9083420SDave Airlie 			   struct dma_fence **fence)
433e9083420SDave Airlie {
434e9083420SDave Airlie 	struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);
435bc9c80feSChristian König 	struct syncobj_wait_entry wait;
436bc9c80feSChristian König 	u64 timeout = nsecs_to_jiffies64(DRM_SYNCOBJ_WAIT_FOR_SUBMIT_TIMEOUT);
437bc9c80feSChristian König 	int ret;
438e9083420SDave Airlie 
439131280a1SEric Anholt 	if (!syncobj)
440131280a1SEric Anholt 		return -ENOENT;
441131280a1SEric Anholt 
4427621350cSChristian König 	/* Waiting for userspace with locks help is illegal cause that can
4437621350cSChristian König 	 * trivial deadlock with page faults for example. Make lockdep complain
4447621350cSChristian König 	 * about it early on.
4457621350cSChristian König 	 */
4467621350cSChristian König 	if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) {
4477621350cSChristian König 		might_sleep();
4487621350cSChristian König 		lockdep_assert_none_held_once();
4497621350cSChristian König 	}
4507621350cSChristian König 
451131280a1SEric Anholt 	*fence = drm_syncobj_fence_get(syncobj);
452bc9c80feSChristian König 
453bc9c80feSChristian König 	if (*fence) {
454bc9c80feSChristian König 		ret = dma_fence_chain_find_seqno(fence, point);
455b19926d4SBas Nieuwenhuizen 		if (!ret) {
456b19926d4SBas Nieuwenhuizen 			/* If the requested seqno is already signaled
457b19926d4SBas Nieuwenhuizen 			 * drm_syncobj_find_fence may return a NULL
458b19926d4SBas Nieuwenhuizen 			 * fence. To make sure the recipient gets
459b19926d4SBas Nieuwenhuizen 			 * signalled, use a new fence instead.
460b19926d4SBas Nieuwenhuizen 			 */
461b19926d4SBas Nieuwenhuizen 			if (!*fence)
462b19926d4SBas Nieuwenhuizen 				*fence = dma_fence_get_stub();
463b19926d4SBas Nieuwenhuizen 
464a37eef63SDaniel Vetter 			goto out;
465b19926d4SBas Nieuwenhuizen 		}
466bc9c80feSChristian König 		dma_fence_put(*fence);
467bc9c80feSChristian König 	} else {
468131280a1SEric Anholt 		ret = -EINVAL;
469131280a1SEric Anholt 	}
470bc9c80feSChristian König 
471bc9c80feSChristian König 	if (!(flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT))
472a37eef63SDaniel Vetter 		goto out;
473bc9c80feSChristian König 
474bc9c80feSChristian König 	memset(&wait, 0, sizeof(wait));
475bc9c80feSChristian König 	wait.task = current;
476bc9c80feSChristian König 	wait.point = point;
477bc9c80feSChristian König 	drm_syncobj_fence_add_wait(syncobj, &wait);
478bc9c80feSChristian König 
479bc9c80feSChristian König 	do {
480bc9c80feSChristian König 		set_current_state(TASK_INTERRUPTIBLE);
481bc9c80feSChristian König 		if (wait.fence) {
482bc9c80feSChristian König 			ret = 0;
483bc9c80feSChristian König 			break;
484bc9c80feSChristian König 		}
485bc9c80feSChristian König                 if (timeout == 0) {
486bc9c80feSChristian König                         ret = -ETIME;
487bc9c80feSChristian König                         break;
488bc9c80feSChristian König                 }
489bc9c80feSChristian König 
490bc9c80feSChristian König 		if (signal_pending(current)) {
491bc9c80feSChristian König 			ret = -ERESTARTSYS;
492bc9c80feSChristian König 			break;
493bc9c80feSChristian König 		}
494bc9c80feSChristian König 
495bc9c80feSChristian König                 timeout = schedule_timeout(timeout);
496bc9c80feSChristian König 	} while (1);
497bc9c80feSChristian König 
498bc9c80feSChristian König 	__set_current_state(TASK_RUNNING);
499bc9c80feSChristian König 	*fence = wait.fence;
500bc9c80feSChristian König 
501bc9c80feSChristian König 	if (wait.node.next)
502bc9c80feSChristian König 		drm_syncobj_remove_wait(syncobj, &wait);
503bc9c80feSChristian König 
504a37eef63SDaniel Vetter out:
505a37eef63SDaniel Vetter 	drm_syncobj_put(syncobj);
506a37eef63SDaniel Vetter 
507e9083420SDave Airlie 	return ret;
508e9083420SDave Airlie }
509afaf5923SJason Ekstrand EXPORT_SYMBOL(drm_syncobj_find_fence);
510e9083420SDave Airlie 
511e9083420SDave Airlie /**
512e9083420SDave Airlie  * drm_syncobj_free - free a sync object.
513e9083420SDave Airlie  * @kref: kref to free.
514e9083420SDave Airlie  *
515e9083420SDave Airlie  * Only to be called from kref_put in drm_syncobj_put.
516e9083420SDave Airlie  */
drm_syncobj_free(struct kref * kref)517e9083420SDave Airlie void drm_syncobj_free(struct kref *kref)
518e9083420SDave Airlie {
519e9083420SDave Airlie 	struct drm_syncobj *syncobj = container_of(kref,
520e9083420SDave Airlie 						   struct drm_syncobj,
521e9083420SDave Airlie 						   refcount);
522c7a47229SSimon Ser 	struct syncobj_eventfd_entry *ev_fd_cur, *ev_fd_tmp;
523c7a47229SSimon Ser 
5240b258ed1SChristian König 	drm_syncobj_replace_fence(syncobj, NULL);
525c7a47229SSimon Ser 
526c7a47229SSimon Ser 	list_for_each_entry_safe(ev_fd_cur, ev_fd_tmp, &syncobj->ev_fd_list, node)
527c7a47229SSimon Ser 		syncobj_eventfd_entry_free(ev_fd_cur);
528c7a47229SSimon Ser 
529e9083420SDave Airlie 	kfree(syncobj);
530e9083420SDave Airlie }
531e9083420SDave Airlie EXPORT_SYMBOL(drm_syncobj_free);
532e9083420SDave Airlie 
5331321fd2cSMarek Olšák /**
5341321fd2cSMarek Olšák  * drm_syncobj_create - create a new syncobj
5351321fd2cSMarek Olšák  * @out_syncobj: returned syncobj
5361321fd2cSMarek Olšák  * @flags: DRM_SYNCOBJ_* flags
5371321fd2cSMarek Olšák  * @fence: if non-NULL, the syncobj will represent this fence
538924fe8dfSDaniel Vetter  *
539924fe8dfSDaniel Vetter  * This is the first function to create a sync object. After creating, drivers
540924fe8dfSDaniel Vetter  * probably want to make it available to userspace, either through
541924fe8dfSDaniel Vetter  * drm_syncobj_get_handle() or drm_syncobj_get_fd().
542924fe8dfSDaniel Vetter  *
543924fe8dfSDaniel Vetter  * Returns 0 on success or a negative error value on failure.
5441321fd2cSMarek Olšák  */
drm_syncobj_create(struct drm_syncobj ** out_syncobj,uint32_t flags,struct dma_fence * fence)5451321fd2cSMarek Olšák int drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags,
5461321fd2cSMarek Olšák 		       struct dma_fence *fence)
547e9083420SDave Airlie {
548fd921693SDavid Stevens 	int ret;
549e9083420SDave Airlie 	struct drm_syncobj *syncobj;
550e9083420SDave Airlie 
551e9083420SDave Airlie 	syncobj = kzalloc(sizeof(struct drm_syncobj), GFP_KERNEL);
552e9083420SDave Airlie 	if (!syncobj)
553e9083420SDave Airlie 		return -ENOMEM;
554e9083420SDave Airlie 
555e9083420SDave Airlie 	kref_init(&syncobj->refcount);
5569c19fb10SJason Ekstrand 	INIT_LIST_HEAD(&syncobj->cb_list);
557c7a47229SSimon Ser 	INIT_LIST_HEAD(&syncobj->ev_fd_list);
558131280a1SEric Anholt 	spin_lock_init(&syncobj->lock);
559e9083420SDave Airlie 
560fd921693SDavid Stevens 	if (flags & DRM_SYNCOBJ_CREATE_SIGNALED) {
561fd921693SDavid Stevens 		ret = drm_syncobj_assign_null_handle(syncobj);
562fd921693SDavid Stevens 		if (ret < 0) {
563fd921693SDavid Stevens 			drm_syncobj_put(syncobj);
564fd921693SDavid Stevens 			return ret;
565fd921693SDavid Stevens 		}
566fd921693SDavid Stevens 	}
5671fc08218SJason Ekstrand 
5681321fd2cSMarek Olšák 	if (fence)
5690b258ed1SChristian König 		drm_syncobj_replace_fence(syncobj, fence);
5701321fd2cSMarek Olšák 
5711321fd2cSMarek Olšák 	*out_syncobj = syncobj;
5721321fd2cSMarek Olšák 	return 0;
5731321fd2cSMarek Olšák }
5741321fd2cSMarek Olšák EXPORT_SYMBOL(drm_syncobj_create);
5751321fd2cSMarek Olšák 
5761321fd2cSMarek Olšák /**
5771321fd2cSMarek Olšák  * drm_syncobj_get_handle - get a handle from a syncobj
578924fe8dfSDaniel Vetter  * @file_private: drm file private pointer
579924fe8dfSDaniel Vetter  * @syncobj: Sync object to export
580924fe8dfSDaniel Vetter  * @handle: out parameter with the new handle
581924fe8dfSDaniel Vetter  *
582924fe8dfSDaniel Vetter  * Exports a sync object created with drm_syncobj_create() as a handle on
583924fe8dfSDaniel Vetter  * @file_private to userspace.
584924fe8dfSDaniel Vetter  *
585924fe8dfSDaniel Vetter  * Returns 0 on success or a negative error value on failure.
5861321fd2cSMarek Olšák  */
drm_syncobj_get_handle(struct drm_file * file_private,struct drm_syncobj * syncobj,u32 * handle)5871321fd2cSMarek Olšák int drm_syncobj_get_handle(struct drm_file *file_private,
5881321fd2cSMarek Olšák 			   struct drm_syncobj *syncobj, u32 *handle)
5891321fd2cSMarek Olšák {
5901321fd2cSMarek Olšák 	int ret;
5911321fd2cSMarek Olšák 
5921321fd2cSMarek Olšák 	/* take a reference to put in the idr */
5931321fd2cSMarek Olšák 	drm_syncobj_get(syncobj);
5941321fd2cSMarek Olšák 
595e9083420SDave Airlie 	idr_preload(GFP_KERNEL);
596e9083420SDave Airlie 	spin_lock(&file_private->syncobj_table_lock);
597e9083420SDave Airlie 	ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT);
598e9083420SDave Airlie 	spin_unlock(&file_private->syncobj_table_lock);
599e9083420SDave Airlie 
600e9083420SDave Airlie 	idr_preload_end();
601e9083420SDave Airlie 
602e9083420SDave Airlie 	if (ret < 0) {
603e9083420SDave Airlie 		drm_syncobj_put(syncobj);
604e9083420SDave Airlie 		return ret;
605e9083420SDave Airlie 	}
606e9083420SDave Airlie 
607e9083420SDave Airlie 	*handle = ret;
608e9083420SDave Airlie 	return 0;
609e9083420SDave Airlie }
6101321fd2cSMarek Olšák EXPORT_SYMBOL(drm_syncobj_get_handle);
6111321fd2cSMarek Olšák 
drm_syncobj_create_as_handle(struct drm_file * file_private,u32 * handle,uint32_t flags)6121321fd2cSMarek Olšák static int drm_syncobj_create_as_handle(struct drm_file *file_private,
6131321fd2cSMarek Olšák 					u32 *handle, uint32_t flags)
6141321fd2cSMarek Olšák {
6151321fd2cSMarek Olšák 	int ret;
6161321fd2cSMarek Olšák 	struct drm_syncobj *syncobj;
6171321fd2cSMarek Olšák 
6181321fd2cSMarek Olšák 	ret = drm_syncobj_create(&syncobj, flags, NULL);
6191321fd2cSMarek Olšák 	if (ret)
6201321fd2cSMarek Olšák 		return ret;
6211321fd2cSMarek Olšák 
6221321fd2cSMarek Olšák 	ret = drm_syncobj_get_handle(file_private, syncobj, handle);
6231321fd2cSMarek Olšák 	drm_syncobj_put(syncobj);
6241321fd2cSMarek Olšák 	return ret;
6251321fd2cSMarek Olšák }
626e9083420SDave Airlie 
drm_syncobj_destroy(struct drm_file * file_private,u32 handle)627e9083420SDave Airlie static int drm_syncobj_destroy(struct drm_file *file_private,
628e9083420SDave Airlie 			       u32 handle)
629e9083420SDave Airlie {
630e9083420SDave Airlie 	struct drm_syncobj *syncobj;
631e9083420SDave Airlie 
632e9083420SDave Airlie 	spin_lock(&file_private->syncobj_table_lock);
633e9083420SDave Airlie 	syncobj = idr_remove(&file_private->syncobj_idr, handle);
634e9083420SDave Airlie 	spin_unlock(&file_private->syncobj_table_lock);
635e9083420SDave Airlie 
636e9083420SDave Airlie 	if (!syncobj)
637e9083420SDave Airlie 		return -EINVAL;
638e9083420SDave Airlie 
639e9083420SDave Airlie 	drm_syncobj_put(syncobj);
640e9083420SDave Airlie 	return 0;
641e9083420SDave Airlie }
642e9083420SDave Airlie 
drm_syncobj_file_release(struct inode * inode,struct file * file)643e9083420SDave Airlie static int drm_syncobj_file_release(struct inode *inode, struct file *file)
644e9083420SDave Airlie {
645e9083420SDave Airlie 	struct drm_syncobj *syncobj = file->private_data;
646e9083420SDave Airlie 
647e9083420SDave Airlie 	drm_syncobj_put(syncobj);
648e9083420SDave Airlie 	return 0;
649e9083420SDave Airlie }
650e9083420SDave Airlie 
651e9083420SDave Airlie static const struct file_operations drm_syncobj_file_fops = {
652e9083420SDave Airlie 	.release = drm_syncobj_file_release,
653e9083420SDave Airlie };
654e9083420SDave Airlie 
655924fe8dfSDaniel Vetter /**
656924fe8dfSDaniel Vetter  * drm_syncobj_get_fd - get a file descriptor from a syncobj
657924fe8dfSDaniel Vetter  * @syncobj: Sync object to export
658924fe8dfSDaniel Vetter  * @p_fd: out parameter with the new file descriptor
659924fe8dfSDaniel Vetter  *
660924fe8dfSDaniel Vetter  * Exports a sync object created with drm_syncobj_create() as a file descriptor.
661924fe8dfSDaniel Vetter  *
662924fe8dfSDaniel Vetter  * Returns 0 on success or a negative error value on failure.
663924fe8dfSDaniel Vetter  */
drm_syncobj_get_fd(struct drm_syncobj * syncobj,int * p_fd)664684fd0afSMarek Olšák int drm_syncobj_get_fd(struct drm_syncobj *syncobj, int *p_fd)
665684fd0afSMarek Olšák {
666e7cdf5c8SChris Wilson 	struct file *file;
667684fd0afSMarek Olšák 	int fd;
668684fd0afSMarek Olšák 
669684fd0afSMarek Olšák 	fd = get_unused_fd_flags(O_CLOEXEC);
670684fd0afSMarek Olšák 	if (fd < 0)
671684fd0afSMarek Olšák 		return fd;
672684fd0afSMarek Olšák 
673e7cdf5c8SChris Wilson 	file = anon_inode_getfile("syncobj_file",
674e7cdf5c8SChris Wilson 				  &drm_syncobj_file_fops,
675e7cdf5c8SChris Wilson 				  syncobj, 0);
676e7cdf5c8SChris Wilson 	if (IS_ERR(file)) {
677684fd0afSMarek Olšák 		put_unused_fd(fd);
678e7cdf5c8SChris Wilson 		return PTR_ERR(file);
679684fd0afSMarek Olšák 	}
680e7cdf5c8SChris Wilson 
681e7cdf5c8SChris Wilson 	drm_syncobj_get(syncobj);
682e7cdf5c8SChris Wilson 	fd_install(fd, file);
683e7cdf5c8SChris Wilson 
684684fd0afSMarek Olšák 	*p_fd = fd;
685684fd0afSMarek Olšák 	return 0;
686684fd0afSMarek Olšák }
687684fd0afSMarek Olšák EXPORT_SYMBOL(drm_syncobj_get_fd);
688684fd0afSMarek Olšák 
drm_syncobj_handle_to_fd(struct drm_file * file_private,u32 handle,int * p_fd)689e9083420SDave Airlie static int drm_syncobj_handle_to_fd(struct drm_file *file_private,
690e9083420SDave Airlie 				    u32 handle, int *p_fd)
691e9083420SDave Airlie {
692e9083420SDave Airlie 	struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);
693e9083420SDave Airlie 	int ret;
694e9083420SDave Airlie 
695e9083420SDave Airlie 	if (!syncobj)
696e9083420SDave Airlie 		return -EINVAL;
697e9083420SDave Airlie 
698684fd0afSMarek Olšák 	ret = drm_syncobj_get_fd(syncobj, p_fd);
699e9083420SDave Airlie 	drm_syncobj_put(syncobj);
700e9083420SDave Airlie 	return ret;
701e9083420SDave Airlie }
702e9083420SDave Airlie 
drm_syncobj_fd_to_handle(struct drm_file * file_private,int fd,u32 * handle)703e9083420SDave Airlie static int drm_syncobj_fd_to_handle(struct drm_file *file_private,
704e9083420SDave Airlie 				    int fd, u32 *handle)
705e9083420SDave Airlie {
706e7cdf5c8SChris Wilson 	struct drm_syncobj *syncobj;
707fb386243SAl Viro 	struct fd f = fdget(fd);
708e9083420SDave Airlie 	int ret;
709e9083420SDave Airlie 
710fb386243SAl Viro 	if (!f.file)
711e9083420SDave Airlie 		return -EINVAL;
712e9083420SDave Airlie 
713fb386243SAl Viro 	if (f.file->f_op != &drm_syncobj_file_fops) {
714fb386243SAl Viro 		fdput(f);
715e7cdf5c8SChris Wilson 		return -EINVAL;
716e7cdf5c8SChris Wilson 	}
717e7cdf5c8SChris Wilson 
718e9083420SDave Airlie 	/* take a reference to put in the idr */
719fb386243SAl Viro 	syncobj = f.file->private_data;
720e9083420SDave Airlie 	drm_syncobj_get(syncobj);
721e9083420SDave Airlie 
722e9083420SDave Airlie 	idr_preload(GFP_KERNEL);
723e9083420SDave Airlie 	spin_lock(&file_private->syncobj_table_lock);
724e9083420SDave Airlie 	ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT);
725e9083420SDave Airlie 	spin_unlock(&file_private->syncobj_table_lock);
726e9083420SDave Airlie 	idr_preload_end();
727e9083420SDave Airlie 
728e7cdf5c8SChris Wilson 	if (ret > 0) {
729e9083420SDave Airlie 		*handle = ret;
730e7cdf5c8SChris Wilson 		ret = 0;
731e7cdf5c8SChris Wilson 	} else
732e7cdf5c8SChris Wilson 		drm_syncobj_put(syncobj);
733e7cdf5c8SChris Wilson 
734fb386243SAl Viro 	fdput(f);
735e7cdf5c8SChris Wilson 	return ret;
736e9083420SDave Airlie }
737e9083420SDave Airlie 
drm_syncobj_import_sync_file_fence(struct drm_file * file_private,int fd,int handle)738a32c94afSVille Syrjälä static int drm_syncobj_import_sync_file_fence(struct drm_file *file_private,
7393ee45a3bSDave Airlie 					      int fd, int handle)
7403ee45a3bSDave Airlie {
7413ee45a3bSDave Airlie 	struct dma_fence *fence = sync_file_get_fence(fd);
7423ee45a3bSDave Airlie 	struct drm_syncobj *syncobj;
7433ee45a3bSDave Airlie 
7443ee45a3bSDave Airlie 	if (!fence)
7453ee45a3bSDave Airlie 		return -EINVAL;
7463ee45a3bSDave Airlie 
7473ee45a3bSDave Airlie 	syncobj = drm_syncobj_find(file_private, handle);
7483ee45a3bSDave Airlie 	if (!syncobj) {
7493ee45a3bSDave Airlie 		dma_fence_put(fence);
7503ee45a3bSDave Airlie 		return -ENOENT;
7513ee45a3bSDave Airlie 	}
7523ee45a3bSDave Airlie 
7530b258ed1SChristian König 	drm_syncobj_replace_fence(syncobj, fence);
7543ee45a3bSDave Airlie 	dma_fence_put(fence);
7553ee45a3bSDave Airlie 	drm_syncobj_put(syncobj);
7563ee45a3bSDave Airlie 	return 0;
7573ee45a3bSDave Airlie }
7583ee45a3bSDave Airlie 
drm_syncobj_export_sync_file(struct drm_file * file_private,int handle,int * p_fd)759a32c94afSVille Syrjälä static int drm_syncobj_export_sync_file(struct drm_file *file_private,
7603ee45a3bSDave Airlie 					int handle, int *p_fd)
7613ee45a3bSDave Airlie {
7623ee45a3bSDave Airlie 	int ret;
7633ee45a3bSDave Airlie 	struct dma_fence *fence;
7643ee45a3bSDave Airlie 	struct sync_file *sync_file;
7653ee45a3bSDave Airlie 	int fd = get_unused_fd_flags(O_CLOEXEC);
7663ee45a3bSDave Airlie 
7673ee45a3bSDave Airlie 	if (fd < 0)
7683ee45a3bSDave Airlie 		return fd;
7693ee45a3bSDave Airlie 
770649fdce2SChunming Zhou 	ret = drm_syncobj_find_fence(file_private, handle, 0, 0, &fence);
7713ee45a3bSDave Airlie 	if (ret)
7723ee45a3bSDave Airlie 		goto err_put_fd;
7733ee45a3bSDave Airlie 
7743ee45a3bSDave Airlie 	sync_file = sync_file_create(fence);
7753ee45a3bSDave Airlie 
7763ee45a3bSDave Airlie 	dma_fence_put(fence);
7773ee45a3bSDave Airlie 
7783ee45a3bSDave Airlie 	if (!sync_file) {
7793ee45a3bSDave Airlie 		ret = -EINVAL;
7803ee45a3bSDave Airlie 		goto err_put_fd;
7813ee45a3bSDave Airlie 	}
7823ee45a3bSDave Airlie 
7833ee45a3bSDave Airlie 	fd_install(fd, sync_file->file);
7843ee45a3bSDave Airlie 
7853ee45a3bSDave Airlie 	*p_fd = fd;
7863ee45a3bSDave Airlie 	return 0;
7873ee45a3bSDave Airlie err_put_fd:
7883ee45a3bSDave Airlie 	put_unused_fd(fd);
7893ee45a3bSDave Airlie 	return ret;
7903ee45a3bSDave Airlie }
791e9083420SDave Airlie /**
7920ae865efSCai Huoqing  * drm_syncobj_open - initializes syncobj file-private structures at devnode open time
793e9083420SDave Airlie  * @file_private: drm file-private structure to set up
794e9083420SDave Airlie  *
795e9083420SDave Airlie  * Called at device open time, sets up the structure for handling refcounting
796e9083420SDave Airlie  * of sync objects.
797e9083420SDave Airlie  */
798e9083420SDave Airlie void
drm_syncobj_open(struct drm_file * file_private)799e9083420SDave Airlie drm_syncobj_open(struct drm_file *file_private)
800e9083420SDave Airlie {
801e86584c5SChris Wilson 	idr_init_base(&file_private->syncobj_idr, 1);
802e9083420SDave Airlie 	spin_lock_init(&file_private->syncobj_table_lock);
803e9083420SDave Airlie }
804e9083420SDave Airlie 
805e9083420SDave Airlie static int
drm_syncobj_release_handle(int id,void * ptr,void * data)806e9083420SDave Airlie drm_syncobj_release_handle(int id, void *ptr, void *data)
807e9083420SDave Airlie {
808e9083420SDave Airlie 	struct drm_syncobj *syncobj = ptr;
809e9083420SDave Airlie 
810e9083420SDave Airlie 	drm_syncobj_put(syncobj);
811e9083420SDave Airlie 	return 0;
812e9083420SDave Airlie }
813e9083420SDave Airlie 
814e9083420SDave Airlie /**
815e9083420SDave Airlie  * drm_syncobj_release - release file-private sync object resources
816e9083420SDave Airlie  * @file_private: drm file-private structure to clean up
817e9083420SDave Airlie  *
818e9083420SDave Airlie  * Called at close time when the filp is going away.
819e9083420SDave Airlie  *
820e9083420SDave Airlie  * Releases any remaining references on objects by this filp.
821e9083420SDave Airlie  */
822e9083420SDave Airlie void
drm_syncobj_release(struct drm_file * file_private)823e9083420SDave Airlie drm_syncobj_release(struct drm_file *file_private)
824e9083420SDave Airlie {
825e9083420SDave Airlie 	idr_for_each(&file_private->syncobj_idr,
826e9083420SDave Airlie 		     &drm_syncobj_release_handle, file_private);
827e9083420SDave Airlie 	idr_destroy(&file_private->syncobj_idr);
828e9083420SDave Airlie }
829e9083420SDave Airlie 
830e9083420SDave Airlie int
drm_syncobj_create_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)831e9083420SDave Airlie drm_syncobj_create_ioctl(struct drm_device *dev, void *data,
832e9083420SDave Airlie 			 struct drm_file *file_private)
833e9083420SDave Airlie {
834e9083420SDave Airlie 	struct drm_syncobj_create *args = data;
835e9083420SDave Airlie 
836e9083420SDave Airlie 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
83769fdf420SChris Wilson 		return -EOPNOTSUPP;
838e9083420SDave Airlie 
839e9083420SDave Airlie 	/* no valid flags yet */
840131280a1SEric Anholt 	if (args->flags & ~DRM_SYNCOBJ_CREATE_SIGNALED)
841e9083420SDave Airlie 		return -EINVAL;
842e9083420SDave Airlie 
8431321fd2cSMarek Olšák 	return drm_syncobj_create_as_handle(file_private,
8441fc08218SJason Ekstrand 					    &args->handle, args->flags);
845e9083420SDave Airlie }
846e9083420SDave Airlie 
847e9083420SDave Airlie int
drm_syncobj_destroy_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)848e9083420SDave Airlie drm_syncobj_destroy_ioctl(struct drm_device *dev, void *data,
849e9083420SDave Airlie 			  struct drm_file *file_private)
850e9083420SDave Airlie {
851e9083420SDave Airlie 	struct drm_syncobj_destroy *args = data;
852e9083420SDave Airlie 
853e9083420SDave Airlie 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
85469fdf420SChris Wilson 		return -EOPNOTSUPP;
855e9083420SDave Airlie 
856e9083420SDave Airlie 	/* make sure padding is empty */
857e9083420SDave Airlie 	if (args->pad)
858e9083420SDave Airlie 		return -EINVAL;
859e9083420SDave Airlie 	return drm_syncobj_destroy(file_private, args->handle);
860e9083420SDave Airlie }
861e9083420SDave Airlie 
862e9083420SDave Airlie int
drm_syncobj_handle_to_fd_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)863e9083420SDave Airlie drm_syncobj_handle_to_fd_ioctl(struct drm_device *dev, void *data,
864e9083420SDave Airlie 				   struct drm_file *file_private)
865e9083420SDave Airlie {
866e9083420SDave Airlie 	struct drm_syncobj_handle *args = data;
867e9083420SDave Airlie 
868e9083420SDave Airlie 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
86969fdf420SChris Wilson 		return -EOPNOTSUPP;
870e9083420SDave Airlie 
8713ee45a3bSDave Airlie 	if (args->pad)
872e9083420SDave Airlie 		return -EINVAL;
873e9083420SDave Airlie 
8743ee45a3bSDave Airlie 	if (args->flags != 0 &&
8753ee45a3bSDave Airlie 	    args->flags != DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE)
8763ee45a3bSDave Airlie 		return -EINVAL;
8773ee45a3bSDave Airlie 
8783ee45a3bSDave Airlie 	if (args->flags & DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE)
8793ee45a3bSDave Airlie 		return drm_syncobj_export_sync_file(file_private, args->handle,
8803ee45a3bSDave Airlie 						    &args->fd);
8813ee45a3bSDave Airlie 
882e9083420SDave Airlie 	return drm_syncobj_handle_to_fd(file_private, args->handle,
883e9083420SDave Airlie 					&args->fd);
884e9083420SDave Airlie }
885e9083420SDave Airlie 
886e9083420SDave Airlie int
drm_syncobj_fd_to_handle_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)887e9083420SDave Airlie drm_syncobj_fd_to_handle_ioctl(struct drm_device *dev, void *data,
888e9083420SDave Airlie 				   struct drm_file *file_private)
889e9083420SDave Airlie {
890e9083420SDave Airlie 	struct drm_syncobj_handle *args = data;
891e9083420SDave Airlie 
892e9083420SDave Airlie 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
89369fdf420SChris Wilson 		return -EOPNOTSUPP;
894e9083420SDave Airlie 
8953ee45a3bSDave Airlie 	if (args->pad)
896e9083420SDave Airlie 		return -EINVAL;
897e9083420SDave Airlie 
8983ee45a3bSDave Airlie 	if (args->flags != 0 &&
8993ee45a3bSDave Airlie 	    args->flags != DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE)
9003ee45a3bSDave Airlie 		return -EINVAL;
9013ee45a3bSDave Airlie 
9023ee45a3bSDave Airlie 	if (args->flags & DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE)
9033ee45a3bSDave Airlie 		return drm_syncobj_import_sync_file_fence(file_private,
9043ee45a3bSDave Airlie 							  args->fd,
9053ee45a3bSDave Airlie 							  args->handle);
9063ee45a3bSDave Airlie 
907e9083420SDave Airlie 	return drm_syncobj_fd_to_handle(file_private, args->fd,
908e9083420SDave Airlie 					&args->handle);
909e9083420SDave Airlie }
9105e60a10eSDave Airlie 
drm_syncobj_transfer_to_timeline(struct drm_file * file_private,struct drm_syncobj_transfer * args)911ea569910SChunming Zhou static int drm_syncobj_transfer_to_timeline(struct drm_file *file_private,
912ea569910SChunming Zhou 					    struct drm_syncobj_transfer *args)
913ea569910SChunming Zhou {
914ea569910SChunming Zhou 	struct drm_syncobj *timeline_syncobj = NULL;
915ec8d985fSChristian König 	struct dma_fence *fence, *tmp;
916ea569910SChunming Zhou 	struct dma_fence_chain *chain;
917ea569910SChunming Zhou 	int ret;
918ea569910SChunming Zhou 
919ea569910SChunming Zhou 	timeline_syncobj = drm_syncobj_find(file_private, args->dst_handle);
920ea569910SChunming Zhou 	if (!timeline_syncobj) {
921ea569910SChunming Zhou 		return -ENOENT;
922ea569910SChunming Zhou 	}
923ea569910SChunming Zhou 	ret = drm_syncobj_find_fence(file_private, args->src_handle,
924ea569910SChunming Zhou 				     args->src_point, args->flags,
925ec8d985fSChristian König 				     &tmp);
926ea569910SChunming Zhou 	if (ret)
927721255b5SChristian König 		goto err_put_timeline;
928721255b5SChristian König 
929ec8d985fSChristian König 	fence = dma_fence_unwrap_merge(tmp);
930ec8d985fSChristian König 	dma_fence_put(tmp);
931f4e3a12bSYang Yingliang 	if (!fence) {
932f4e3a12bSYang Yingliang 		ret = -ENOMEM;
933ec8d985fSChristian König 		goto err_put_timeline;
934f4e3a12bSYang Yingliang 	}
935721255b5SChristian König 
936440d0f12SChristian König 	chain = dma_fence_chain_alloc();
937ea569910SChunming Zhou 	if (!chain) {
938ea569910SChunming Zhou 		ret = -ENOMEM;
939721255b5SChristian König 		goto err_free_fence;
940ea569910SChunming Zhou 	}
941721255b5SChristian König 
942ea569910SChunming Zhou 	drm_syncobj_add_point(timeline_syncobj, chain, fence, args->dst_point);
943721255b5SChristian König err_free_fence:
944ea569910SChunming Zhou 	dma_fence_put(fence);
945721255b5SChristian König err_put_timeline:
946ea569910SChunming Zhou 	drm_syncobj_put(timeline_syncobj);
947ea569910SChunming Zhou 
948ea569910SChunming Zhou 	return ret;
949ea569910SChunming Zhou }
950ea569910SChunming Zhou 
951ea569910SChunming Zhou static int
drm_syncobj_transfer_to_binary(struct drm_file * file_private,struct drm_syncobj_transfer * args)952ea569910SChunming Zhou drm_syncobj_transfer_to_binary(struct drm_file *file_private,
953ea569910SChunming Zhou 			       struct drm_syncobj_transfer *args)
954ea569910SChunming Zhou {
955ea569910SChunming Zhou 	struct drm_syncobj *binary_syncobj = NULL;
956ea569910SChunming Zhou 	struct dma_fence *fence;
957ea569910SChunming Zhou 	int ret;
958ea569910SChunming Zhou 
959ea569910SChunming Zhou 	binary_syncobj = drm_syncobj_find(file_private, args->dst_handle);
960ea569910SChunming Zhou 	if (!binary_syncobj)
961ea569910SChunming Zhou 		return -ENOENT;
962ea569910SChunming Zhou 	ret = drm_syncobj_find_fence(file_private, args->src_handle,
963ea569910SChunming Zhou 				     args->src_point, args->flags, &fence);
964ea569910SChunming Zhou 	if (ret)
965ea569910SChunming Zhou 		goto err;
966ea569910SChunming Zhou 	drm_syncobj_replace_fence(binary_syncobj, fence);
967ea569910SChunming Zhou 	dma_fence_put(fence);
968ea569910SChunming Zhou err:
969ea569910SChunming Zhou 	drm_syncobj_put(binary_syncobj);
970ea569910SChunming Zhou 
971ea569910SChunming Zhou 	return ret;
972ea569910SChunming Zhou }
973ea569910SChunming Zhou int
drm_syncobj_transfer_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)974ea569910SChunming Zhou drm_syncobj_transfer_ioctl(struct drm_device *dev, void *data,
975ea569910SChunming Zhou 			   struct drm_file *file_private)
976ea569910SChunming Zhou {
977ea569910SChunming Zhou 	struct drm_syncobj_transfer *args = data;
978ea569910SChunming Zhou 	int ret;
979ea569910SChunming Zhou 
980060cebb2SLionel Landwerlin 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
9815ec77638SLionel Landwerlin 		return -EOPNOTSUPP;
982ea569910SChunming Zhou 
983ea569910SChunming Zhou 	if (args->pad)
984ea569910SChunming Zhou 		return -EINVAL;
985ea569910SChunming Zhou 
986ea569910SChunming Zhou 	if (args->dst_point)
987ea569910SChunming Zhou 		ret = drm_syncobj_transfer_to_timeline(file_private, args);
988ea569910SChunming Zhou 	else
989ea569910SChunming Zhou 		ret = drm_syncobj_transfer_to_binary(file_private, args);
990ea569910SChunming Zhou 
991ea569910SChunming Zhou 	return ret;
992ea569910SChunming Zhou }
993ea569910SChunming Zhou 
syncobj_wait_fence_func(struct dma_fence * fence,struct dma_fence_cb * cb)994e7aca503SJason Ekstrand static void syncobj_wait_fence_func(struct dma_fence *fence,
995e7aca503SJason Ekstrand 				    struct dma_fence_cb *cb)
996e7aca503SJason Ekstrand {
997e7aca503SJason Ekstrand 	struct syncobj_wait_entry *wait =
998e7aca503SJason Ekstrand 		container_of(cb, struct syncobj_wait_entry, fence_cb);
999e7aca503SJason Ekstrand 
1000e7aca503SJason Ekstrand 	wake_up_process(wait->task);
1001e7aca503SJason Ekstrand }
1002e7aca503SJason Ekstrand 
syncobj_wait_syncobj_func(struct drm_syncobj * syncobj,struct syncobj_wait_entry * wait)1003e7aca503SJason Ekstrand static void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj,
100461a98b1bSChristian König 				      struct syncobj_wait_entry *wait)
1005e7aca503SJason Ekstrand {
100601d6c357SChunming Zhou 	struct dma_fence *fence;
100701d6c357SChunming Zhou 
1008131280a1SEric Anholt 	/* This happens inside the syncobj lock */
100901d6c357SChunming Zhou 	fence = rcu_dereference_protected(syncobj->fence,
101001d6c357SChunming Zhou 					  lockdep_is_held(&syncobj->lock));
101101d6c357SChunming Zhou 	dma_fence_get(fence);
101201d6c357SChunming Zhou 	if (!fence || dma_fence_chain_find_seqno(&fence, wait->point)) {
101301d6c357SChunming Zhou 		dma_fence_put(fence);
101401d6c357SChunming Zhou 		return;
101501d6c357SChunming Zhou 	} else if (!fence) {
101601d6c357SChunming Zhou 		wait->fence = dma_fence_get_stub();
101701d6c357SChunming Zhou 	} else {
101801d6c357SChunming Zhou 		wait->fence = fence;
101901d6c357SChunming Zhou 	}
102001d6c357SChunming Zhou 
1021e7aca503SJason Ekstrand 	wake_up_process(wait->task);
102201d6c357SChunming Zhou 	list_del_init(&wait->node);
1023e7aca503SJason Ekstrand }
1024e7aca503SJason 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)1025e7aca503SJason Ekstrand static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs,
102601d6c357SChunming Zhou 						  void __user *user_points,
1027e7aca503SJason Ekstrand 						  uint32_t count,
1028e7aca503SJason Ekstrand 						  uint32_t flags,
1029e7aca503SJason Ekstrand 						  signed long timeout,
1030e7aca503SJason Ekstrand 						  uint32_t *idx)
1031e7aca503SJason Ekstrand {
1032e7aca503SJason Ekstrand 	struct syncobj_wait_entry *entries;
1033e7aca503SJason Ekstrand 	struct dma_fence *fence;
103401d6c357SChunming Zhou 	uint64_t *points;
1035e7aca503SJason Ekstrand 	uint32_t signaled_count, i;
1036e7aca503SJason Ekstrand 
1037716cfee8SErik Kurzinger 	if (flags & (DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
1038716cfee8SErik Kurzinger 		     DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE))
10397621350cSChristian König 		lockdep_assert_none_held_once();
10407621350cSChristian König 
104101d6c357SChunming Zhou 	points = kmalloc_array(count, sizeof(*points), GFP_KERNEL);
104201d6c357SChunming Zhou 	if (points == NULL)
1043e7aca503SJason Ekstrand 		return -ENOMEM;
1044e7aca503SJason Ekstrand 
104501d6c357SChunming Zhou 	if (!user_points) {
104601d6c357SChunming Zhou 		memset(points, 0, count * sizeof(uint64_t));
104701d6c357SChunming Zhou 
104801d6c357SChunming Zhou 	} else if (copy_from_user(points, user_points,
104901d6c357SChunming Zhou 				  sizeof(uint64_t) * count)) {
105001d6c357SChunming Zhou 		timeout = -EFAULT;
105101d6c357SChunming Zhou 		goto err_free_points;
105201d6c357SChunming Zhou 	}
105301d6c357SChunming Zhou 
105401d6c357SChunming Zhou 	entries = kcalloc(count, sizeof(*entries), GFP_KERNEL);
105501d6c357SChunming Zhou 	if (!entries) {
105601d6c357SChunming Zhou 		timeout = -ENOMEM;
105701d6c357SChunming Zhou 		goto err_free_points;
105801d6c357SChunming Zhou 	}
1059e7aca503SJason Ekstrand 	/* Walk the list of sync objects and initialize entries.  We do
1060e7aca503SJason Ekstrand 	 * this up-front so that we can properly return -EINVAL if there is
1061e7aca503SJason Ekstrand 	 * a syncobj with a missing fence and then never have the chance of
1062e7aca503SJason Ekstrand 	 * returning -EINVAL again.
1063e7aca503SJason Ekstrand 	 */
1064e7aca503SJason Ekstrand 	signaled_count = 0;
1065e7aca503SJason Ekstrand 	for (i = 0; i < count; ++i) {
106601d6c357SChunming Zhou 		struct dma_fence *fence;
106701d6c357SChunming Zhou 
1068e7aca503SJason Ekstrand 		entries[i].task = current;
106901d6c357SChunming Zhou 		entries[i].point = points[i];
107001d6c357SChunming Zhou 		fence = drm_syncobj_fence_get(syncobjs[i]);
107101d6c357SChunming Zhou 		if (!fence || dma_fence_chain_find_seqno(&fence, points[i])) {
107201d6c357SChunming Zhou 			dma_fence_put(fence);
10732341c645SErik Kurzinger 			if (flags & (DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
10742341c645SErik Kurzinger 				     DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE)) {
1075e7aca503SJason Ekstrand 				continue;
1076e7aca503SJason Ekstrand 			} else {
107712fec62aSChris Wilson 				timeout = -EINVAL;
1078e7aca503SJason Ekstrand 				goto cleanup_entries;
1079e7aca503SJason Ekstrand 			}
1080e7aca503SJason Ekstrand 		}
1081e7aca503SJason Ekstrand 
108201d6c357SChunming Zhou 		if (fence)
108301d6c357SChunming Zhou 			entries[i].fence = fence;
108401d6c357SChunming Zhou 		else
108501d6c357SChunming Zhou 			entries[i].fence = dma_fence_get_stub();
108601d6c357SChunming Zhou 
108701d6c357SChunming Zhou 		if ((flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE) ||
108801d6c357SChunming Zhou 		    dma_fence_is_signaled(entries[i].fence)) {
1089e7aca503SJason Ekstrand 			if (signaled_count == 0 && idx)
1090e7aca503SJason Ekstrand 				*idx = i;
1091e7aca503SJason Ekstrand 			signaled_count++;
1092e7aca503SJason Ekstrand 		}
1093e7aca503SJason Ekstrand 	}
1094e7aca503SJason Ekstrand 
1095e7aca503SJason Ekstrand 	if (signaled_count == count ||
1096e7aca503SJason Ekstrand 	    (signaled_count > 0 &&
1097e7aca503SJason Ekstrand 	     !(flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL)))
1098e7aca503SJason Ekstrand 		goto cleanup_entries;
1099e7aca503SJason Ekstrand 
1100e7aca503SJason Ekstrand 	/* There's a very annoying laxness in the dma_fence API here, in
1101e7aca503SJason Ekstrand 	 * that backends are not required to automatically report when a
1102e7aca503SJason Ekstrand 	 * fence is signaled prior to fence->ops->enable_signaling() being
1103e7aca503SJason Ekstrand 	 * called.  So here if we fail to match signaled_count, we need to
1104e7aca503SJason Ekstrand 	 * fallthough and try a 0 timeout wait!
1105e7aca503SJason Ekstrand 	 */
1106e7aca503SJason Ekstrand 
1107716cfee8SErik Kurzinger 	if (flags & (DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
1108716cfee8SErik Kurzinger 		     DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE)) {
110961a98b1bSChristian König 		for (i = 0; i < count; ++i)
111061a98b1bSChristian König 			drm_syncobj_fence_add_wait(syncobjs[i], &entries[i]);
1111e7aca503SJason Ekstrand 	}
1112e7aca503SJason Ekstrand 
1113e7aca503SJason Ekstrand 	do {
1114e7aca503SJason Ekstrand 		set_current_state(TASK_INTERRUPTIBLE);
1115e7aca503SJason Ekstrand 
1116e7aca503SJason Ekstrand 		signaled_count = 0;
1117e7aca503SJason Ekstrand 		for (i = 0; i < count; ++i) {
1118e7aca503SJason Ekstrand 			fence = entries[i].fence;
1119e7aca503SJason Ekstrand 			if (!fence)
1120e7aca503SJason Ekstrand 				continue;
1121e7aca503SJason Ekstrand 
112201d6c357SChunming Zhou 			if ((flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE) ||
112301d6c357SChunming Zhou 			    dma_fence_is_signaled(fence) ||
1124e7aca503SJason Ekstrand 			    (!entries[i].fence_cb.func &&
1125e7aca503SJason Ekstrand 			     dma_fence_add_callback(fence,
1126e7aca503SJason Ekstrand 						    &entries[i].fence_cb,
1127e7aca503SJason Ekstrand 						    syncobj_wait_fence_func))) {
1128e7aca503SJason Ekstrand 				/* The fence has been signaled */
1129e7aca503SJason Ekstrand 				if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL) {
1130e7aca503SJason Ekstrand 					signaled_count++;
1131e7aca503SJason Ekstrand 				} else {
1132e7aca503SJason Ekstrand 					if (idx)
1133e7aca503SJason Ekstrand 						*idx = i;
1134e7aca503SJason Ekstrand 					goto done_waiting;
1135e7aca503SJason Ekstrand 				}
1136e7aca503SJason Ekstrand 			}
1137e7aca503SJason Ekstrand 		}
1138e7aca503SJason Ekstrand 
1139e7aca503SJason Ekstrand 		if (signaled_count == count)
1140e7aca503SJason Ekstrand 			goto done_waiting;
1141e7aca503SJason Ekstrand 
1142e7aca503SJason Ekstrand 		if (timeout == 0) {
114312fec62aSChris Wilson 			timeout = -ETIME;
1144e7aca503SJason Ekstrand 			goto done_waiting;
1145e7aca503SJason Ekstrand 		}
1146e7aca503SJason Ekstrand 
114712fec62aSChris Wilson 		if (signal_pending(current)) {
114812fec62aSChris Wilson 			timeout = -ERESTARTSYS;
114912fec62aSChris Wilson 			goto done_waiting;
115012fec62aSChris Wilson 		}
1151e7aca503SJason Ekstrand 
115212fec62aSChris Wilson 		timeout = schedule_timeout(timeout);
115312fec62aSChris Wilson 	} while (1);
1154e7aca503SJason Ekstrand 
1155e7aca503SJason Ekstrand done_waiting:
1156e7aca503SJason Ekstrand 	__set_current_state(TASK_RUNNING);
1157e7aca503SJason Ekstrand 
1158e7aca503SJason Ekstrand cleanup_entries:
1159e7aca503SJason Ekstrand 	for (i = 0; i < count; ++i) {
116061a98b1bSChristian König 		drm_syncobj_remove_wait(syncobjs[i], &entries[i]);
1161e7aca503SJason Ekstrand 		if (entries[i].fence_cb.func)
1162e7aca503SJason Ekstrand 			dma_fence_remove_callback(entries[i].fence,
1163e7aca503SJason Ekstrand 						  &entries[i].fence_cb);
1164e7aca503SJason Ekstrand 		dma_fence_put(entries[i].fence);
1165e7aca503SJason Ekstrand 	}
1166e7aca503SJason Ekstrand 	kfree(entries);
1167e7aca503SJason Ekstrand 
116801d6c357SChunming Zhou err_free_points:
116901d6c357SChunming Zhou 	kfree(points);
117001d6c357SChunming Zhou 
117112fec62aSChris Wilson 	return timeout;
1172e7aca503SJason Ekstrand }
1173e7aca503SJason Ekstrand 
11745e60a10eSDave Airlie /**
11755e60a10eSDave Airlie  * drm_timeout_abs_to_jiffies - calculate jiffies timeout from absolute value
11765e60a10eSDave Airlie  *
11775e60a10eSDave Airlie  * @timeout_nsec: timeout nsec component in ns, 0 for poll
11785e60a10eSDave Airlie  *
11795e60a10eSDave Airlie  * Calculate the timeout in jiffies from an absolute time in sec/nsec.
11805e60a10eSDave Airlie  */
drm_timeout_abs_to_jiffies(int64_t timeout_nsec)1181877b3729SQiang Yu signed long drm_timeout_abs_to_jiffies(int64_t timeout_nsec)
11825e60a10eSDave Airlie {
11835e60a10eSDave Airlie 	ktime_t abs_timeout, now;
11845e60a10eSDave Airlie 	u64 timeout_ns, timeout_jiffies64;
11855e60a10eSDave Airlie 
11865e60a10eSDave Airlie 	/* make 0 timeout means poll - absolute 0 doesn't seem valid */
11875e60a10eSDave Airlie 	if (timeout_nsec == 0)
11885e60a10eSDave Airlie 		return 0;
11895e60a10eSDave Airlie 
11905e60a10eSDave Airlie 	abs_timeout = ns_to_ktime(timeout_nsec);
11915e60a10eSDave Airlie 	now = ktime_get();
11925e60a10eSDave Airlie 
11935e60a10eSDave Airlie 	if (!ktime_after(abs_timeout, now))
11945e60a10eSDave Airlie 		return 0;
11955e60a10eSDave Airlie 
11965e60a10eSDave Airlie 	timeout_ns = ktime_to_ns(ktime_sub(abs_timeout, now));
11975e60a10eSDave Airlie 
11985e60a10eSDave Airlie 	timeout_jiffies64 = nsecs_to_jiffies64(timeout_ns);
11995e60a10eSDave Airlie 	/*  clamp timeout to avoid infinite timeout */
12005e60a10eSDave Airlie 	if (timeout_jiffies64 >= MAX_SCHEDULE_TIMEOUT - 1)
12015e60a10eSDave Airlie 		return MAX_SCHEDULE_TIMEOUT - 1;
12025e60a10eSDave Airlie 
12035e60a10eSDave Airlie 	return timeout_jiffies64 + 1;
12045e60a10eSDave Airlie }
1205877b3729SQiang Yu EXPORT_SYMBOL(drm_timeout_abs_to_jiffies);
12065e60a10eSDave 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)1207e7aca503SJason Ekstrand static int drm_syncobj_array_wait(struct drm_device *dev,
12085e60a10eSDave Airlie 				  struct drm_file *file_private,
12095e60a10eSDave Airlie 				  struct drm_syncobj_wait *wait,
121001d6c357SChunming Zhou 				  struct drm_syncobj_timeline_wait *timeline_wait,
121101d6c357SChunming Zhou 				  struct drm_syncobj **syncobjs, bool timeline)
12125e60a10eSDave Airlie {
121301d6c357SChunming Zhou 	signed long timeout = 0;
12145e60a10eSDave Airlie 	uint32_t first = ~0;
12155e60a10eSDave Airlie 
121601d6c357SChunming Zhou 	if (!timeline) {
121701d6c357SChunming Zhou 		timeout = drm_timeout_abs_to_jiffies(wait->timeout_nsec);
121812fec62aSChris Wilson 		timeout = drm_syncobj_array_wait_timeout(syncobjs,
121901d6c357SChunming Zhou 							 NULL,
12205e60a10eSDave Airlie 							 wait->count_handles,
1221e7aca503SJason Ekstrand 							 wait->flags,
1222e7aca503SJason Ekstrand 							 timeout, &first);
122312fec62aSChris Wilson 		if (timeout < 0)
122412fec62aSChris Wilson 			return timeout;
12255e60a10eSDave Airlie 		wait->first_signaled = first;
122601d6c357SChunming Zhou 	} else {
122701d6c357SChunming Zhou 		timeout = drm_timeout_abs_to_jiffies(timeline_wait->timeout_nsec);
122801d6c357SChunming Zhou 		timeout = drm_syncobj_array_wait_timeout(syncobjs,
122901d6c357SChunming Zhou 							 u64_to_user_ptr(timeline_wait->points),
123001d6c357SChunming Zhou 							 timeline_wait->count_handles,
123101d6c357SChunming Zhou 							 timeline_wait->flags,
123201d6c357SChunming Zhou 							 timeout, &first);
123301d6c357SChunming Zhou 		if (timeout < 0)
123401d6c357SChunming Zhou 			return timeout;
123501d6c357SChunming Zhou 		timeline_wait->first_signaled = first;
123601d6c357SChunming Zhou 	}
12375e60a10eSDave Airlie 	return 0;
12385e60a10eSDave Airlie }
12395e60a10eSDave Airlie 
drm_syncobj_array_find(struct drm_file * file_private,void __user * user_handles,uint32_t count_handles,struct drm_syncobj *** syncobjs_out)12403e6fb72dSJason Ekstrand static int drm_syncobj_array_find(struct drm_file *file_private,
12419e554462SVille Syrjälä 				  void __user *user_handles,
12429e554462SVille Syrjälä 				  uint32_t count_handles,
12433e6fb72dSJason Ekstrand 				  struct drm_syncobj ***syncobjs_out)
12443e6fb72dSJason Ekstrand {
12453e6fb72dSJason Ekstrand 	uint32_t i, *handles;
12463e6fb72dSJason Ekstrand 	struct drm_syncobj **syncobjs;
12473e6fb72dSJason Ekstrand 	int ret;
12483e6fb72dSJason Ekstrand 
12493e6fb72dSJason Ekstrand 	handles = kmalloc_array(count_handles, sizeof(*handles), GFP_KERNEL);
12503e6fb72dSJason Ekstrand 	if (handles == NULL)
12513e6fb72dSJason Ekstrand 		return -ENOMEM;
12523e6fb72dSJason Ekstrand 
12533e6fb72dSJason Ekstrand 	if (copy_from_user(handles, user_handles,
12543e6fb72dSJason Ekstrand 			   sizeof(uint32_t) * count_handles)) {
12553e6fb72dSJason Ekstrand 		ret = -EFAULT;
12563e6fb72dSJason Ekstrand 		goto err_free_handles;
12573e6fb72dSJason Ekstrand 	}
12583e6fb72dSJason Ekstrand 
12593e6fb72dSJason Ekstrand 	syncobjs = kmalloc_array(count_handles, sizeof(*syncobjs), GFP_KERNEL);
12603e6fb72dSJason Ekstrand 	if (syncobjs == NULL) {
12613e6fb72dSJason Ekstrand 		ret = -ENOMEM;
12623e6fb72dSJason Ekstrand 		goto err_free_handles;
12633e6fb72dSJason Ekstrand 	}
12643e6fb72dSJason Ekstrand 
12653e6fb72dSJason Ekstrand 	for (i = 0; i < count_handles; i++) {
12663e6fb72dSJason Ekstrand 		syncobjs[i] = drm_syncobj_find(file_private, handles[i]);
12673e6fb72dSJason Ekstrand 		if (!syncobjs[i]) {
12683e6fb72dSJason Ekstrand 			ret = -ENOENT;
12693e6fb72dSJason Ekstrand 			goto err_put_syncobjs;
12703e6fb72dSJason Ekstrand 		}
12713e6fb72dSJason Ekstrand 	}
12723e6fb72dSJason Ekstrand 
12733e6fb72dSJason Ekstrand 	kfree(handles);
12743e6fb72dSJason Ekstrand 	*syncobjs_out = syncobjs;
12753e6fb72dSJason Ekstrand 	return 0;
12763e6fb72dSJason Ekstrand 
12773e6fb72dSJason Ekstrand err_put_syncobjs:
12783e6fb72dSJason Ekstrand 	while (i-- > 0)
12793e6fb72dSJason Ekstrand 		drm_syncobj_put(syncobjs[i]);
12803e6fb72dSJason Ekstrand 	kfree(syncobjs);
12813e6fb72dSJason Ekstrand err_free_handles:
12823e6fb72dSJason Ekstrand 	kfree(handles);
12833e6fb72dSJason Ekstrand 
12843e6fb72dSJason Ekstrand 	return ret;
12853e6fb72dSJason Ekstrand }
12863e6fb72dSJason Ekstrand 
drm_syncobj_array_free(struct drm_syncobj ** syncobjs,uint32_t count)12873e6fb72dSJason Ekstrand static void drm_syncobj_array_free(struct drm_syncobj **syncobjs,
12883e6fb72dSJason Ekstrand 				   uint32_t count)
12893e6fb72dSJason Ekstrand {
12903e6fb72dSJason Ekstrand 	uint32_t i;
1291948de842SSuraj Upadhyay 
12923e6fb72dSJason Ekstrand 	for (i = 0; i < count; i++)
12933e6fb72dSJason Ekstrand 		drm_syncobj_put(syncobjs[i]);
12943e6fb72dSJason Ekstrand 	kfree(syncobjs);
12953e6fb72dSJason Ekstrand }
12963e6fb72dSJason Ekstrand 
12975e60a10eSDave Airlie int
drm_syncobj_wait_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)12985e60a10eSDave Airlie drm_syncobj_wait_ioctl(struct drm_device *dev, void *data,
12995e60a10eSDave Airlie 		       struct drm_file *file_private)
13005e60a10eSDave Airlie {
13015e60a10eSDave Airlie 	struct drm_syncobj_wait *args = data;
1302e7aca503SJason Ekstrand 	struct drm_syncobj **syncobjs;
13035e60a10eSDave Airlie 	int ret = 0;
13045e60a10eSDave Airlie 
13055e60a10eSDave Airlie 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
130669fdf420SChris Wilson 		return -EOPNOTSUPP;
13075e60a10eSDave Airlie 
1308e7aca503SJason Ekstrand 	if (args->flags & ~(DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL |
1309e7aca503SJason Ekstrand 			    DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT))
13105e60a10eSDave Airlie 		return -EINVAL;
13115e60a10eSDave Airlie 
13125e60a10eSDave Airlie 	if (args->count_handles == 0)
13135e60a10eSDave Airlie 		return -EINVAL;
13145e60a10eSDave Airlie 
13153e6fb72dSJason Ekstrand 	ret = drm_syncobj_array_find(file_private,
13165e60a10eSDave Airlie 				     u64_to_user_ptr(args->handles),
13173e6fb72dSJason Ekstrand 				     args->count_handles,
13183e6fb72dSJason Ekstrand 				     &syncobjs);
13193e6fb72dSJason Ekstrand 	if (ret < 0)
13203e6fb72dSJason Ekstrand 		return ret;
13215e60a10eSDave Airlie 
1322e7aca503SJason Ekstrand 	ret = drm_syncobj_array_wait(dev, file_private,
132301d6c357SChunming Zhou 				     args, NULL, syncobjs, false);
13245e60a10eSDave Airlie 
13253e6fb72dSJason Ekstrand 	drm_syncobj_array_free(syncobjs, args->count_handles);
13265e60a10eSDave Airlie 
13275e60a10eSDave Airlie 	return ret;
13285e60a10eSDave Airlie }
1329aa4035d2SJason Ekstrand 
1330aa4035d2SJason Ekstrand int
drm_syncobj_timeline_wait_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)133101d6c357SChunming Zhou drm_syncobj_timeline_wait_ioctl(struct drm_device *dev, void *data,
133201d6c357SChunming Zhou 				struct drm_file *file_private)
133301d6c357SChunming Zhou {
133401d6c357SChunming Zhou 	struct drm_syncobj_timeline_wait *args = data;
133501d6c357SChunming Zhou 	struct drm_syncobj **syncobjs;
133601d6c357SChunming Zhou 	int ret = 0;
133701d6c357SChunming Zhou 
1338060cebb2SLionel Landwerlin 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
13395ec77638SLionel Landwerlin 		return -EOPNOTSUPP;
134001d6c357SChunming Zhou 
134101d6c357SChunming Zhou 	if (args->flags & ~(DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL |
134201d6c357SChunming Zhou 			    DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
134301d6c357SChunming Zhou 			    DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE))
134401d6c357SChunming Zhou 		return -EINVAL;
134501d6c357SChunming Zhou 
134601d6c357SChunming Zhou 	if (args->count_handles == 0)
134701d6c357SChunming Zhou 		return -EINVAL;
134801d6c357SChunming Zhou 
134901d6c357SChunming Zhou 	ret = drm_syncobj_array_find(file_private,
135001d6c357SChunming Zhou 				     u64_to_user_ptr(args->handles),
135101d6c357SChunming Zhou 				     args->count_handles,
135201d6c357SChunming Zhou 				     &syncobjs);
135301d6c357SChunming Zhou 	if (ret < 0)
135401d6c357SChunming Zhou 		return ret;
135501d6c357SChunming Zhou 
135601d6c357SChunming Zhou 	ret = drm_syncobj_array_wait(dev, file_private,
135701d6c357SChunming Zhou 				     NULL, args, syncobjs, true);
135801d6c357SChunming Zhou 
135901d6c357SChunming Zhou 	drm_syncobj_array_free(syncobjs, args->count_handles);
136001d6c357SChunming Zhou 
136101d6c357SChunming Zhou 	return ret;
136201d6c357SChunming Zhou }
136301d6c357SChunming Zhou 
syncobj_eventfd_entry_fence_func(struct dma_fence * fence,struct dma_fence_cb * cb)1364c7a47229SSimon Ser static void syncobj_eventfd_entry_fence_func(struct dma_fence *fence,
1365c7a47229SSimon Ser 					     struct dma_fence_cb *cb)
1366c7a47229SSimon Ser {
1367c7a47229SSimon Ser 	struct syncobj_eventfd_entry *entry =
1368c7a47229SSimon Ser 		container_of(cb, struct syncobj_eventfd_entry, fence_cb);
1369c7a47229SSimon Ser 
1370c7a47229SSimon Ser 	eventfd_signal(entry->ev_fd_ctx, 1);
1371c7a47229SSimon Ser 	syncobj_eventfd_entry_free(entry);
1372c7a47229SSimon Ser }
1373c7a47229SSimon Ser 
1374c7a47229SSimon Ser static void
syncobj_eventfd_entry_func(struct drm_syncobj * syncobj,struct syncobj_eventfd_entry * entry)1375c7a47229SSimon Ser syncobj_eventfd_entry_func(struct drm_syncobj *syncobj,
1376c7a47229SSimon Ser 			   struct syncobj_eventfd_entry *entry)
1377c7a47229SSimon Ser {
1378c7a47229SSimon Ser 	int ret;
1379c7a47229SSimon Ser 	struct dma_fence *fence;
1380c7a47229SSimon Ser 
1381c7a47229SSimon Ser 	/* This happens inside the syncobj lock */
1382c7a47229SSimon Ser 	fence = dma_fence_get(rcu_dereference_protected(syncobj->fence, 1));
1383*20e1e1a2SErik Kurzinger 	if (!fence)
1384*20e1e1a2SErik Kurzinger 		return;
1385*20e1e1a2SErik Kurzinger 
1386c7a47229SSimon Ser 	ret = dma_fence_chain_find_seqno(&fence, entry->point);
1387*20e1e1a2SErik Kurzinger 	if (ret != 0) {
1388*20e1e1a2SErik Kurzinger 		/* The given seqno has not been submitted yet. */
1389c7a47229SSimon Ser 		dma_fence_put(fence);
1390c7a47229SSimon Ser 		return;
1391*20e1e1a2SErik Kurzinger 	} else if (!fence) {
1392*20e1e1a2SErik Kurzinger 		/* If dma_fence_chain_find_seqno returns 0 but sets the fence
1393*20e1e1a2SErik Kurzinger 		 * to NULL, it implies that the given seqno is signaled and a
1394*20e1e1a2SErik Kurzinger 		 * later seqno has already been submitted. Assign a stub fence
1395*20e1e1a2SErik Kurzinger 		 * so that the eventfd still gets signaled below.
1396*20e1e1a2SErik Kurzinger 		 */
1397*20e1e1a2SErik Kurzinger 		fence = dma_fence_get_stub();
1398c7a47229SSimon Ser 	}
1399c7a47229SSimon Ser 
1400c7a47229SSimon Ser 	list_del_init(&entry->node);
1401c7a47229SSimon Ser 	entry->fence = fence;
1402c7a47229SSimon Ser 
1403c7a47229SSimon Ser 	if (entry->flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE) {
1404c7a47229SSimon Ser 		eventfd_signal(entry->ev_fd_ctx, 1);
1405c7a47229SSimon Ser 		syncobj_eventfd_entry_free(entry);
1406c7a47229SSimon Ser 	} else {
1407c7a47229SSimon Ser 		ret = dma_fence_add_callback(fence, &entry->fence_cb,
1408c7a47229SSimon Ser 					     syncobj_eventfd_entry_fence_func);
1409c7a47229SSimon Ser 		if (ret == -ENOENT) {
1410c7a47229SSimon Ser 			eventfd_signal(entry->ev_fd_ctx, 1);
1411c7a47229SSimon Ser 			syncobj_eventfd_entry_free(entry);
1412c7a47229SSimon Ser 		}
1413c7a47229SSimon Ser 	}
1414c7a47229SSimon Ser }
1415c7a47229SSimon Ser 
1416c7a47229SSimon Ser int
drm_syncobj_eventfd_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)1417c7a47229SSimon Ser drm_syncobj_eventfd_ioctl(struct drm_device *dev, void *data,
1418c7a47229SSimon Ser 			  struct drm_file *file_private)
1419c7a47229SSimon Ser {
1420c7a47229SSimon Ser 	struct drm_syncobj_eventfd *args = data;
1421c7a47229SSimon Ser 	struct drm_syncobj *syncobj;
1422c7a47229SSimon Ser 	struct eventfd_ctx *ev_fd_ctx;
1423c7a47229SSimon Ser 	struct syncobj_eventfd_entry *entry;
1424c7a47229SSimon Ser 
1425c7a47229SSimon Ser 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
1426c7a47229SSimon Ser 		return -EOPNOTSUPP;
1427c7a47229SSimon Ser 
1428c7a47229SSimon Ser 	if (args->flags & ~DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE)
1429c7a47229SSimon Ser 		return -EINVAL;
1430c7a47229SSimon Ser 
1431c7a47229SSimon Ser 	if (args->pad)
1432c7a47229SSimon Ser 		return -EINVAL;
1433c7a47229SSimon Ser 
1434c7a47229SSimon Ser 	syncobj = drm_syncobj_find(file_private, args->handle);
1435c7a47229SSimon Ser 	if (!syncobj)
1436c7a47229SSimon Ser 		return -ENOENT;
1437c7a47229SSimon Ser 
1438c7a47229SSimon Ser 	ev_fd_ctx = eventfd_ctx_fdget(args->fd);
1439c7a47229SSimon Ser 	if (IS_ERR(ev_fd_ctx))
1440c7a47229SSimon Ser 		return PTR_ERR(ev_fd_ctx);
1441c7a47229SSimon Ser 
1442c7a47229SSimon Ser 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
1443c7a47229SSimon Ser 	if (!entry) {
1444c7a47229SSimon Ser 		eventfd_ctx_put(ev_fd_ctx);
1445c7a47229SSimon Ser 		return -ENOMEM;
1446c7a47229SSimon Ser 	}
1447c7a47229SSimon Ser 	entry->syncobj = syncobj;
1448c7a47229SSimon Ser 	entry->ev_fd_ctx = ev_fd_ctx;
1449c7a47229SSimon Ser 	entry->point = args->point;
1450c7a47229SSimon Ser 	entry->flags = args->flags;
1451c7a47229SSimon Ser 
1452c7a47229SSimon Ser 	drm_syncobj_add_eventfd(syncobj, entry);
1453c7a47229SSimon Ser 	drm_syncobj_put(syncobj);
1454c7a47229SSimon Ser 
1455c7a47229SSimon Ser 	return 0;
1456c7a47229SSimon Ser }
145701d6c357SChunming Zhou 
145801d6c357SChunming Zhou int
drm_syncobj_reset_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)1459aa4035d2SJason Ekstrand drm_syncobj_reset_ioctl(struct drm_device *dev, void *data,
1460aa4035d2SJason Ekstrand 			struct drm_file *file_private)
1461aa4035d2SJason Ekstrand {
1462aa4035d2SJason Ekstrand 	struct drm_syncobj_array *args = data;
1463aa4035d2SJason Ekstrand 	struct drm_syncobj **syncobjs;
1464aa4035d2SJason Ekstrand 	uint32_t i;
1465aa4035d2SJason Ekstrand 	int ret;
1466aa4035d2SJason Ekstrand 
1467aa4035d2SJason Ekstrand 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
146869fdf420SChris Wilson 		return -EOPNOTSUPP;
1469aa4035d2SJason Ekstrand 
1470aa4035d2SJason Ekstrand 	if (args->pad != 0)
1471aa4035d2SJason Ekstrand 		return -EINVAL;
1472aa4035d2SJason Ekstrand 
1473aa4035d2SJason Ekstrand 	if (args->count_handles == 0)
1474aa4035d2SJason Ekstrand 		return -EINVAL;
1475aa4035d2SJason Ekstrand 
1476aa4035d2SJason Ekstrand 	ret = drm_syncobj_array_find(file_private,
1477aa4035d2SJason Ekstrand 				     u64_to_user_ptr(args->handles),
1478aa4035d2SJason Ekstrand 				     args->count_handles,
1479aa4035d2SJason Ekstrand 				     &syncobjs);
1480aa4035d2SJason Ekstrand 	if (ret < 0)
1481aa4035d2SJason Ekstrand 		return ret;
1482aa4035d2SJason Ekstrand 
1483131280a1SEric Anholt 	for (i = 0; i < args->count_handles; i++)
14840b258ed1SChristian König 		drm_syncobj_replace_fence(syncobjs[i], NULL);
1485131280a1SEric Anholt 
1486aa4035d2SJason Ekstrand 	drm_syncobj_array_free(syncobjs, args->count_handles);
1487aa4035d2SJason Ekstrand 
1488131280a1SEric Anholt 	return 0;
1489aa4035d2SJason Ekstrand }
1490ffa9443fSJason Ekstrand 
1491ffa9443fSJason Ekstrand int
drm_syncobj_signal_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)1492ffa9443fSJason Ekstrand drm_syncobj_signal_ioctl(struct drm_device *dev, void *data,
1493ffa9443fSJason Ekstrand 			 struct drm_file *file_private)
1494ffa9443fSJason Ekstrand {
1495ffa9443fSJason Ekstrand 	struct drm_syncobj_array *args = data;
1496ffa9443fSJason Ekstrand 	struct drm_syncobj **syncobjs;
1497ffa9443fSJason Ekstrand 	uint32_t i;
1498ffa9443fSJason Ekstrand 	int ret;
1499ffa9443fSJason Ekstrand 
1500ffa9443fSJason Ekstrand 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
150169fdf420SChris Wilson 		return -EOPNOTSUPP;
1502ffa9443fSJason Ekstrand 
1503ffa9443fSJason Ekstrand 	if (args->pad != 0)
1504ffa9443fSJason Ekstrand 		return -EINVAL;
1505ffa9443fSJason Ekstrand 
1506ffa9443fSJason Ekstrand 	if (args->count_handles == 0)
1507ffa9443fSJason Ekstrand 		return -EINVAL;
1508ffa9443fSJason Ekstrand 
1509ffa9443fSJason Ekstrand 	ret = drm_syncobj_array_find(file_private,
1510ffa9443fSJason Ekstrand 				     u64_to_user_ptr(args->handles),
1511ffa9443fSJason Ekstrand 				     args->count_handles,
1512ffa9443fSJason Ekstrand 				     &syncobjs);
1513ffa9443fSJason Ekstrand 	if (ret < 0)
1514ffa9443fSJason Ekstrand 		return ret;
1515ffa9443fSJason Ekstrand 
1516fd921693SDavid Stevens 	for (i = 0; i < args->count_handles; i++) {
1517fd921693SDavid Stevens 		ret = drm_syncobj_assign_null_handle(syncobjs[i]);
1518fd921693SDavid Stevens 		if (ret < 0)
1519fd921693SDavid Stevens 			break;
1520fd921693SDavid Stevens 	}
1521ffa9443fSJason Ekstrand 
1522ffa9443fSJason Ekstrand 	drm_syncobj_array_free(syncobjs, args->count_handles);
1523ffa9443fSJason Ekstrand 
1524ffa9443fSJason Ekstrand 	return ret;
1525ffa9443fSJason Ekstrand }
152627b575a9SChunming Zhou 
152750d1ebefSChunming Zhou int
drm_syncobj_timeline_signal_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)152850d1ebefSChunming Zhou drm_syncobj_timeline_signal_ioctl(struct drm_device *dev, void *data,
152950d1ebefSChunming Zhou 				  struct drm_file *file_private)
153050d1ebefSChunming Zhou {
153150d1ebefSChunming Zhou 	struct drm_syncobj_timeline_array *args = data;
153250d1ebefSChunming Zhou 	struct drm_syncobj **syncobjs;
153350d1ebefSChunming Zhou 	struct dma_fence_chain **chains;
153450d1ebefSChunming Zhou 	uint64_t *points;
153550d1ebefSChunming Zhou 	uint32_t i, j;
153650d1ebefSChunming Zhou 	int ret;
153750d1ebefSChunming Zhou 
1538060cebb2SLionel Landwerlin 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
153950d1ebefSChunming Zhou 		return -EOPNOTSUPP;
154050d1ebefSChunming Zhou 
15412093dea3SChunming Zhou 	if (args->flags != 0)
154250d1ebefSChunming Zhou 		return -EINVAL;
154350d1ebefSChunming Zhou 
154450d1ebefSChunming Zhou 	if (args->count_handles == 0)
154550d1ebefSChunming Zhou 		return -EINVAL;
154650d1ebefSChunming Zhou 
154750d1ebefSChunming Zhou 	ret = drm_syncobj_array_find(file_private,
154850d1ebefSChunming Zhou 				     u64_to_user_ptr(args->handles),
154950d1ebefSChunming Zhou 				     args->count_handles,
155050d1ebefSChunming Zhou 				     &syncobjs);
155150d1ebefSChunming Zhou 	if (ret < 0)
155250d1ebefSChunming Zhou 		return ret;
155350d1ebefSChunming Zhou 
155450d1ebefSChunming Zhou 	points = kmalloc_array(args->count_handles, sizeof(*points),
155550d1ebefSChunming Zhou 			       GFP_KERNEL);
155650d1ebefSChunming Zhou 	if (!points) {
155750d1ebefSChunming Zhou 		ret = -ENOMEM;
155850d1ebefSChunming Zhou 		goto out;
155950d1ebefSChunming Zhou 	}
156050d1ebefSChunming Zhou 	if (!u64_to_user_ptr(args->points)) {
156150d1ebefSChunming Zhou 		memset(points, 0, args->count_handles * sizeof(uint64_t));
156250d1ebefSChunming Zhou 	} else if (copy_from_user(points, u64_to_user_ptr(args->points),
156350d1ebefSChunming Zhou 				  sizeof(uint64_t) * args->count_handles)) {
156450d1ebefSChunming Zhou 		ret = -EFAULT;
156550d1ebefSChunming Zhou 		goto err_points;
156650d1ebefSChunming Zhou 	}
156750d1ebefSChunming Zhou 
156850d1ebefSChunming Zhou 	chains = kmalloc_array(args->count_handles, sizeof(void *), GFP_KERNEL);
156950d1ebefSChunming Zhou 	if (!chains) {
157050d1ebefSChunming Zhou 		ret = -ENOMEM;
157150d1ebefSChunming Zhou 		goto err_points;
157250d1ebefSChunming Zhou 	}
157350d1ebefSChunming Zhou 	for (i = 0; i < args->count_handles; i++) {
1574440d0f12SChristian König 		chains[i] = dma_fence_chain_alloc();
157550d1ebefSChunming Zhou 		if (!chains[i]) {
157650d1ebefSChunming Zhou 			for (j = 0; j < i; j++)
1577440d0f12SChristian König 				dma_fence_chain_free(chains[j]);
157850d1ebefSChunming Zhou 			ret = -ENOMEM;
157950d1ebefSChunming Zhou 			goto err_chains;
158050d1ebefSChunming Zhou 		}
158150d1ebefSChunming Zhou 	}
158250d1ebefSChunming Zhou 
158350d1ebefSChunming Zhou 	for (i = 0; i < args->count_handles; i++) {
158450d1ebefSChunming Zhou 		struct dma_fence *fence = dma_fence_get_stub();
158550d1ebefSChunming Zhou 
158650d1ebefSChunming Zhou 		drm_syncobj_add_point(syncobjs[i], chains[i],
158750d1ebefSChunming Zhou 				      fence, points[i]);
158850d1ebefSChunming Zhou 		dma_fence_put(fence);
158950d1ebefSChunming Zhou 	}
159050d1ebefSChunming Zhou err_chains:
159150d1ebefSChunming Zhou 	kfree(chains);
159250d1ebefSChunming Zhou err_points:
159350d1ebefSChunming Zhou 	kfree(points);
159450d1ebefSChunming Zhou out:
159550d1ebefSChunming Zhou 	drm_syncobj_array_free(syncobjs, args->count_handles);
159650d1ebefSChunming Zhou 
159750d1ebefSChunming Zhou 	return ret;
159850d1ebefSChunming Zhou }
159950d1ebefSChunming Zhou 
drm_syncobj_query_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)160027b575a9SChunming Zhou int drm_syncobj_query_ioctl(struct drm_device *dev, void *data,
160127b575a9SChunming Zhou 			    struct drm_file *file_private)
160227b575a9SChunming Zhou {
160327b575a9SChunming Zhou 	struct drm_syncobj_timeline_array *args = data;
160427b575a9SChunming Zhou 	struct drm_syncobj **syncobjs;
160527b575a9SChunming Zhou 	uint64_t __user *points = u64_to_user_ptr(args->points);
160627b575a9SChunming Zhou 	uint32_t i;
160727b575a9SChunming Zhou 	int ret;
160827b575a9SChunming Zhou 
1609060cebb2SLionel Landwerlin 	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
1610060cebb2SLionel Landwerlin 		return -EOPNOTSUPP;
161127b575a9SChunming Zhou 
16122093dea3SChunming Zhou 	if (args->flags & ~DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED)
161327b575a9SChunming Zhou 		return -EINVAL;
161427b575a9SChunming Zhou 
161527b575a9SChunming Zhou 	if (args->count_handles == 0)
161627b575a9SChunming Zhou 		return -EINVAL;
161727b575a9SChunming Zhou 
161827b575a9SChunming Zhou 	ret = drm_syncobj_array_find(file_private,
161927b575a9SChunming Zhou 				     u64_to_user_ptr(args->handles),
162027b575a9SChunming Zhou 				     args->count_handles,
162127b575a9SChunming Zhou 				     &syncobjs);
162227b575a9SChunming Zhou 	if (ret < 0)
162327b575a9SChunming Zhou 		return ret;
162427b575a9SChunming Zhou 
162527b575a9SChunming Zhou 	for (i = 0; i < args->count_handles; i++) {
162627b575a9SChunming Zhou 		struct dma_fence_chain *chain;
162727b575a9SChunming Zhou 		struct dma_fence *fence;
162827b575a9SChunming Zhou 		uint64_t point;
162927b575a9SChunming Zhou 
163027b575a9SChunming Zhou 		fence = drm_syncobj_fence_get(syncobjs[i]);
163127b575a9SChunming Zhou 		chain = to_dma_fence_chain(fence);
163227b575a9SChunming Zhou 		if (chain) {
16332093dea3SChunming Zhou 			struct dma_fence *iter, *last_signaled =
16342093dea3SChunming Zhou 				dma_fence_get(fence);
163527b575a9SChunming Zhou 
16362093dea3SChunming Zhou 			if (args->flags &
16372093dea3SChunming Zhou 			    DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED) {
16382093dea3SChunming Zhou 				point = fence->seqno;
16392093dea3SChunming Zhou 			} else {
164027b575a9SChunming Zhou 				dma_fence_chain_for_each(iter, fence) {
1641b33b556cSChristian König 					if (iter->context != fence->context) {
1642b33b556cSChristian König 						dma_fence_put(iter);
164327b575a9SChunming Zhou 						/* It is most likely that timeline has
164427b575a9SChunming Zhou 						* unorder points. */
164527b575a9SChunming Zhou 						break;
164627b575a9SChunming Zhou 					}
1647b33b556cSChristian König 					dma_fence_put(last_signaled);
1648b33b556cSChristian König 					last_signaled = dma_fence_get(iter);
1649b33b556cSChristian König 				}
165027b575a9SChunming Zhou 				point = dma_fence_is_signaled(last_signaled) ?
165127b575a9SChunming Zhou 					last_signaled->seqno :
165227b575a9SChunming Zhou 					to_dma_fence_chain(last_signaled)->prev_seqno;
16532093dea3SChunming Zhou 			}
165427b575a9SChunming Zhou 			dma_fence_put(last_signaled);
165527b575a9SChunming Zhou 		} else {
165627b575a9SChunming Zhou 			point = 0;
165727b575a9SChunming Zhou 		}
16582093dea3SChunming Zhou 		dma_fence_put(fence);
165927b575a9SChunming Zhou 		ret = copy_to_user(&points[i], &point, sizeof(uint64_t));
166027b575a9SChunming Zhou 		ret = ret ? -EFAULT : 0;
166127b575a9SChunming Zhou 		if (ret)
166227b575a9SChunming Zhou 			break;
166327b575a9SChunming Zhou 	}
166427b575a9SChunming Zhou 	drm_syncobj_array_free(syncobjs, args->count_handles);
166527b575a9SChunming Zhou 
166627b575a9SChunming Zhou 	return ret;
166727b575a9SChunming Zhou }
1668