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