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