162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright 2017 Red Hat
362306a36Sopenharmony_ci * Parts ported from amdgpu (fence wait code).
462306a36Sopenharmony_ci * Copyright 2016 Advanced Micro Devices, Inc.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
762306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
862306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation
962306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1062306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
1162306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the next
1462306a36Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
1562306a36Sopenharmony_ci * Software.
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1862306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1962306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
2062306a36Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2162306a36Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2262306a36Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2362306a36Sopenharmony_ci * IN THE SOFTWARE.
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * Authors:
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/**
3062306a36Sopenharmony_ci * DOC: Overview
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * DRM synchronisation objects (syncobj, see struct &drm_syncobj) provide a
3362306a36Sopenharmony_ci * container for a synchronization primitive which can be used by userspace
3462306a36Sopenharmony_ci * to explicitly synchronize GPU commands, can be shared between userspace
3562306a36Sopenharmony_ci * processes, and can be shared between different DRM drivers.
3662306a36Sopenharmony_ci * Their primary use-case is to implement Vulkan fences and semaphores.
3762306a36Sopenharmony_ci * The syncobj userspace API provides ioctls for several operations:
3862306a36Sopenharmony_ci *
3962306a36Sopenharmony_ci *  - Creation and destruction of syncobjs
4062306a36Sopenharmony_ci *  - Import and export of syncobjs to/from a syncobj file descriptor
4162306a36Sopenharmony_ci *  - Import and export a syncobj's underlying fence to/from a sync file
4262306a36Sopenharmony_ci *  - Reset a syncobj (set its fence to NULL)
4362306a36Sopenharmony_ci *  - Signal a syncobj (set a trivially signaled fence)
4462306a36Sopenharmony_ci *  - Wait for a syncobj's fence to appear and be signaled
4562306a36Sopenharmony_ci *
4662306a36Sopenharmony_ci * The syncobj userspace API also provides operations to manipulate a syncobj
4762306a36Sopenharmony_ci * in terms of a timeline of struct &dma_fence_chain rather than a single
4862306a36Sopenharmony_ci * struct &dma_fence, through the following operations:
4962306a36Sopenharmony_ci *
5062306a36Sopenharmony_ci *   - Signal a given point on the timeline
5162306a36Sopenharmony_ci *   - Wait for a given point to appear and/or be signaled
5262306a36Sopenharmony_ci *   - Import and export from/to a given point of a timeline
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * At it's core, a syncobj is simply a wrapper around a pointer to a struct
5562306a36Sopenharmony_ci * &dma_fence which may be NULL.
5662306a36Sopenharmony_ci * When a syncobj is first created, its pointer is either NULL or a pointer
5762306a36Sopenharmony_ci * to an already signaled fence depending on whether the
5862306a36Sopenharmony_ci * &DRM_SYNCOBJ_CREATE_SIGNALED flag is passed to
5962306a36Sopenharmony_ci * &DRM_IOCTL_SYNCOBJ_CREATE.
6062306a36Sopenharmony_ci *
6162306a36Sopenharmony_ci * If the syncobj is considered as a binary (its state is either signaled or
6262306a36Sopenharmony_ci * unsignaled) primitive, when GPU work is enqueued in a DRM driver to signal
6362306a36Sopenharmony_ci * the syncobj, the syncobj's fence is replaced with a fence which will be
6462306a36Sopenharmony_ci * signaled by the completion of that work.
6562306a36Sopenharmony_ci * If the syncobj is considered as a timeline primitive, when GPU work is
6662306a36Sopenharmony_ci * enqueued in a DRM driver to signal the a given point of the syncobj, a new
6762306a36Sopenharmony_ci * struct &dma_fence_chain pointing to the DRM driver's fence and also
6862306a36Sopenharmony_ci * pointing to the previous fence that was in the syncobj. The new struct
6962306a36Sopenharmony_ci * &dma_fence_chain fence replace the syncobj's fence and will be signaled by
7062306a36Sopenharmony_ci * completion of the DRM driver's work and also any work associated with the
7162306a36Sopenharmony_ci * fence previously in the syncobj.
7262306a36Sopenharmony_ci *
7362306a36Sopenharmony_ci * When GPU work which waits on a syncobj is enqueued in a DRM driver, at the
7462306a36Sopenharmony_ci * time the work is enqueued, it waits on the syncobj's fence before
7562306a36Sopenharmony_ci * submitting the work to hardware. That fence is either :
7662306a36Sopenharmony_ci *
7762306a36Sopenharmony_ci *    - The syncobj's current fence if the syncobj is considered as a binary
7862306a36Sopenharmony_ci *      primitive.
7962306a36Sopenharmony_ci *    - The struct &dma_fence associated with a given point if the syncobj is
8062306a36Sopenharmony_ci *      considered as a timeline primitive.
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci * If the syncobj's fence is NULL or not present in the syncobj's timeline,
8362306a36Sopenharmony_ci * the enqueue operation is expected to fail.
8462306a36Sopenharmony_ci *
8562306a36Sopenharmony_ci * With binary syncobj, all manipulation of the syncobjs's fence happens in
8662306a36Sopenharmony_ci * terms of the current fence at the time the ioctl is called by userspace
8762306a36Sopenharmony_ci * regardless of whether that operation is an immediate host-side operation
8862306a36Sopenharmony_ci * (signal or reset) or or an operation which is enqueued in some driver
8962306a36Sopenharmony_ci * queue. &DRM_IOCTL_SYNCOBJ_RESET and &DRM_IOCTL_SYNCOBJ_SIGNAL can be used
9062306a36Sopenharmony_ci * to manipulate a syncobj from the host by resetting its pointer to NULL or
9162306a36Sopenharmony_ci * setting its pointer to a fence which is already signaled.
9262306a36Sopenharmony_ci *
9362306a36Sopenharmony_ci * With a timeline syncobj, all manipulation of the synobj's fence happens in
9462306a36Sopenharmony_ci * terms of a u64 value referring to point in the timeline. See
9562306a36Sopenharmony_ci * dma_fence_chain_find_seqno() to see how a given point is found in the
9662306a36Sopenharmony_ci * timeline.
9762306a36Sopenharmony_ci *
9862306a36Sopenharmony_ci * Note that applications should be careful to always use timeline set of
9962306a36Sopenharmony_ci * ioctl() when dealing with syncobj considered as timeline. Using a binary
10062306a36Sopenharmony_ci * set of ioctl() with a syncobj considered as timeline could result incorrect
10162306a36Sopenharmony_ci * synchronization. The use of binary syncobj is supported through the
10262306a36Sopenharmony_ci * timeline set of ioctl() by using a point value of 0, this will reproduce
10362306a36Sopenharmony_ci * the behavior of the binary set of ioctl() (for example replace the
10462306a36Sopenharmony_ci * syncobj's fence when signaling).
10562306a36Sopenharmony_ci *
10662306a36Sopenharmony_ci *
10762306a36Sopenharmony_ci * Host-side wait on syncobjs
10862306a36Sopenharmony_ci * --------------------------
10962306a36Sopenharmony_ci *
11062306a36Sopenharmony_ci * &DRM_IOCTL_SYNCOBJ_WAIT takes an array of syncobj handles and does a
11162306a36Sopenharmony_ci * host-side wait on all of the syncobj fences simultaneously.
11262306a36Sopenharmony_ci * If &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL is set, the wait ioctl will wait on
11362306a36Sopenharmony_ci * all of the syncobj fences to be signaled before it returns.
11462306a36Sopenharmony_ci * Otherwise, it returns once at least one syncobj fence has been signaled
11562306a36Sopenharmony_ci * and the index of a signaled fence is written back to the client.
11662306a36Sopenharmony_ci *
11762306a36Sopenharmony_ci * Unlike the enqueued GPU work dependencies which fail if they see a NULL
11862306a36Sopenharmony_ci * fence in a syncobj, if &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT is set,
11962306a36Sopenharmony_ci * the host-side wait will first wait for the syncobj to receive a non-NULL
12062306a36Sopenharmony_ci * fence and then wait on that fence.
12162306a36Sopenharmony_ci * If &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT is not set and any one of the
12262306a36Sopenharmony_ci * syncobjs in the array has a NULL fence, -EINVAL will be returned.
12362306a36Sopenharmony_ci * Assuming the syncobj starts off with a NULL fence, this allows a client
12462306a36Sopenharmony_ci * to do a host wait in one thread (or process) which waits on GPU work
12562306a36Sopenharmony_ci * submitted in another thread (or process) without having to manually
12662306a36Sopenharmony_ci * synchronize between the two.
12762306a36Sopenharmony_ci * This requirement is inherited from the Vulkan fence API.
12862306a36Sopenharmony_ci *
12962306a36Sopenharmony_ci * Similarly, &DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT takes an array of syncobj
13062306a36Sopenharmony_ci * handles as well as an array of u64 points and does a host-side wait on all
13162306a36Sopenharmony_ci * of syncobj fences at the given points simultaneously.
13262306a36Sopenharmony_ci *
13362306a36Sopenharmony_ci * &DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT also adds the ability to wait for a given
13462306a36Sopenharmony_ci * fence to materialize on the timeline without waiting for the fence to be
13562306a36Sopenharmony_ci * signaled by using the &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE flag. This
13662306a36Sopenharmony_ci * requirement is inherited from the wait-before-signal behavior required by
13762306a36Sopenharmony_ci * the Vulkan timeline semaphore API.
13862306a36Sopenharmony_ci *
13962306a36Sopenharmony_ci * Alternatively, &DRM_IOCTL_SYNCOBJ_EVENTFD can be used to wait without
14062306a36Sopenharmony_ci * blocking: an eventfd will be signaled when the syncobj is. This is useful to
14162306a36Sopenharmony_ci * integrate the wait in an event loop.
14262306a36Sopenharmony_ci *
14362306a36Sopenharmony_ci *
14462306a36Sopenharmony_ci * Import/export of syncobjs
14562306a36Sopenharmony_ci * -------------------------
14662306a36Sopenharmony_ci *
14762306a36Sopenharmony_ci * &DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE and &DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD
14862306a36Sopenharmony_ci * provide two mechanisms for import/export of syncobjs.
14962306a36Sopenharmony_ci *
15062306a36Sopenharmony_ci * The first lets the client import or export an entire syncobj to a file
15162306a36Sopenharmony_ci * descriptor.
15262306a36Sopenharmony_ci * These fd's are opaque and have no other use case, except passing the
15362306a36Sopenharmony_ci * syncobj between processes.
15462306a36Sopenharmony_ci * All exported file descriptors and any syncobj handles created as a
15562306a36Sopenharmony_ci * result of importing those file descriptors own a reference to the
15662306a36Sopenharmony_ci * same underlying struct &drm_syncobj and the syncobj can be used
15762306a36Sopenharmony_ci * persistently across all the processes with which it is shared.
15862306a36Sopenharmony_ci * The syncobj is freed only once the last reference is dropped.
15962306a36Sopenharmony_ci * Unlike dma-buf, importing a syncobj creates a new handle (with its own
16062306a36Sopenharmony_ci * reference) for every import instead of de-duplicating.
16162306a36Sopenharmony_ci * The primary use-case of this persistent import/export is for shared
16262306a36Sopenharmony_ci * Vulkan fences and semaphores.
16362306a36Sopenharmony_ci *
16462306a36Sopenharmony_ci * The second import/export mechanism, which is indicated by
16562306a36Sopenharmony_ci * &DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE or
16662306a36Sopenharmony_ci * &DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE lets the client
16762306a36Sopenharmony_ci * import/export the syncobj's current fence from/to a &sync_file.
16862306a36Sopenharmony_ci * When a syncobj is exported to a sync file, that sync file wraps the
16962306a36Sopenharmony_ci * sycnobj's fence at the time of export and any later signal or reset
17062306a36Sopenharmony_ci * operations on the syncobj will not affect the exported sync file.
17162306a36Sopenharmony_ci * When a sync file is imported into a syncobj, the syncobj's fence is set
17262306a36Sopenharmony_ci * to the fence wrapped by that sync file.
17362306a36Sopenharmony_ci * Because sync files are immutable, resetting or signaling the syncobj
17462306a36Sopenharmony_ci * will not affect any sync files whose fences have been imported into the
17562306a36Sopenharmony_ci * syncobj.
17662306a36Sopenharmony_ci *
17762306a36Sopenharmony_ci *
17862306a36Sopenharmony_ci * Import/export of timeline points in timeline syncobjs
17962306a36Sopenharmony_ci * -----------------------------------------------------
18062306a36Sopenharmony_ci *
18162306a36Sopenharmony_ci * &DRM_IOCTL_SYNCOBJ_TRANSFER provides a mechanism to transfer a struct
18262306a36Sopenharmony_ci * &dma_fence_chain of a syncobj at a given u64 point to another u64 point
18362306a36Sopenharmony_ci * into another syncobj.
18462306a36Sopenharmony_ci *
18562306a36Sopenharmony_ci * Note that if you want to transfer a struct &dma_fence_chain from a given
18662306a36Sopenharmony_ci * point on a timeline syncobj from/into a binary syncobj, you can use the
18762306a36Sopenharmony_ci * point 0 to mean take/replace the fence in the syncobj.
18862306a36Sopenharmony_ci */
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci#include <linux/anon_inodes.h>
19162306a36Sopenharmony_ci#include <linux/dma-fence-unwrap.h>
19262306a36Sopenharmony_ci#include <linux/eventfd.h>
19362306a36Sopenharmony_ci#include <linux/file.h>
19462306a36Sopenharmony_ci#include <linux/fs.h>
19562306a36Sopenharmony_ci#include <linux/sched/signal.h>
19662306a36Sopenharmony_ci#include <linux/sync_file.h>
19762306a36Sopenharmony_ci#include <linux/uaccess.h>
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci#include <drm/drm.h>
20062306a36Sopenharmony_ci#include <drm/drm_drv.h>
20162306a36Sopenharmony_ci#include <drm/drm_file.h>
20262306a36Sopenharmony_ci#include <drm/drm_gem.h>
20362306a36Sopenharmony_ci#include <drm/drm_print.h>
20462306a36Sopenharmony_ci#include <drm/drm_syncobj.h>
20562306a36Sopenharmony_ci#include <drm/drm_utils.h>
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci#include "drm_internal.h"
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistruct syncobj_wait_entry {
21062306a36Sopenharmony_ci	struct list_head node;
21162306a36Sopenharmony_ci	struct task_struct *task;
21262306a36Sopenharmony_ci	struct dma_fence *fence;
21362306a36Sopenharmony_ci	struct dma_fence_cb fence_cb;
21462306a36Sopenharmony_ci	u64    point;
21562306a36Sopenharmony_ci};
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj,
21862306a36Sopenharmony_ci				      struct syncobj_wait_entry *wait);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistruct syncobj_eventfd_entry {
22162306a36Sopenharmony_ci	struct list_head node;
22262306a36Sopenharmony_ci	struct dma_fence *fence;
22362306a36Sopenharmony_ci	struct dma_fence_cb fence_cb;
22462306a36Sopenharmony_ci	struct drm_syncobj *syncobj;
22562306a36Sopenharmony_ci	struct eventfd_ctx *ev_fd_ctx;
22662306a36Sopenharmony_ci	u64 point;
22762306a36Sopenharmony_ci	u32 flags;
22862306a36Sopenharmony_ci};
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic void
23162306a36Sopenharmony_cisyncobj_eventfd_entry_func(struct drm_syncobj *syncobj,
23262306a36Sopenharmony_ci			   struct syncobj_eventfd_entry *entry);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/**
23562306a36Sopenharmony_ci * drm_syncobj_find - lookup and reference a sync object.
23662306a36Sopenharmony_ci * @file_private: drm file private pointer
23762306a36Sopenharmony_ci * @handle: sync object handle to lookup.
23862306a36Sopenharmony_ci *
23962306a36Sopenharmony_ci * Returns a reference to the syncobj pointed to by handle or NULL. The
24062306a36Sopenharmony_ci * reference must be released by calling drm_syncobj_put().
24162306a36Sopenharmony_ci */
24262306a36Sopenharmony_cistruct drm_syncobj *drm_syncobj_find(struct drm_file *file_private,
24362306a36Sopenharmony_ci				     u32 handle)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	struct drm_syncobj *syncobj;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	spin_lock(&file_private->syncobj_table_lock);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	/* Check if we currently have a reference on the object */
25062306a36Sopenharmony_ci	syncobj = idr_find(&file_private->syncobj_idr, handle);
25162306a36Sopenharmony_ci	if (syncobj)
25262306a36Sopenharmony_ci		drm_syncobj_get(syncobj);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	spin_unlock(&file_private->syncobj_table_lock);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return syncobj;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_syncobj_find);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic void drm_syncobj_fence_add_wait(struct drm_syncobj *syncobj,
26162306a36Sopenharmony_ci				       struct syncobj_wait_entry *wait)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct dma_fence *fence;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (wait->fence)
26662306a36Sopenharmony_ci		return;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	spin_lock(&syncobj->lock);
26962306a36Sopenharmony_ci	/* We've already tried once to get a fence and failed.  Now that we
27062306a36Sopenharmony_ci	 * have the lock, try one more time just to be sure we don't add a
27162306a36Sopenharmony_ci	 * callback when a fence has already been set.
27262306a36Sopenharmony_ci	 */
27362306a36Sopenharmony_ci	fence = dma_fence_get(rcu_dereference_protected(syncobj->fence, 1));
27462306a36Sopenharmony_ci	if (!fence || dma_fence_chain_find_seqno(&fence, wait->point)) {
27562306a36Sopenharmony_ci		dma_fence_put(fence);
27662306a36Sopenharmony_ci		list_add_tail(&wait->node, &syncobj->cb_list);
27762306a36Sopenharmony_ci	} else if (!fence) {
27862306a36Sopenharmony_ci		wait->fence = dma_fence_get_stub();
27962306a36Sopenharmony_ci	} else {
28062306a36Sopenharmony_ci		wait->fence = fence;
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci	spin_unlock(&syncobj->lock);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic void drm_syncobj_remove_wait(struct drm_syncobj *syncobj,
28662306a36Sopenharmony_ci				    struct syncobj_wait_entry *wait)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	if (!wait->node.next)
28962306a36Sopenharmony_ci		return;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	spin_lock(&syncobj->lock);
29262306a36Sopenharmony_ci	list_del_init(&wait->node);
29362306a36Sopenharmony_ci	spin_unlock(&syncobj->lock);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic void
29762306a36Sopenharmony_cisyncobj_eventfd_entry_free(struct syncobj_eventfd_entry *entry)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	eventfd_ctx_put(entry->ev_fd_ctx);
30062306a36Sopenharmony_ci	dma_fence_put(entry->fence);
30162306a36Sopenharmony_ci	/* This happens either inside the syncobj lock, or after the node has
30262306a36Sopenharmony_ci	 * already been removed from the list.
30362306a36Sopenharmony_ci	 */
30462306a36Sopenharmony_ci	list_del(&entry->node);
30562306a36Sopenharmony_ci	kfree(entry);
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic void
30962306a36Sopenharmony_cidrm_syncobj_add_eventfd(struct drm_syncobj *syncobj,
31062306a36Sopenharmony_ci			struct syncobj_eventfd_entry *entry)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	spin_lock(&syncobj->lock);
31362306a36Sopenharmony_ci	list_add_tail(&entry->node, &syncobj->ev_fd_list);
31462306a36Sopenharmony_ci	syncobj_eventfd_entry_func(syncobj, entry);
31562306a36Sopenharmony_ci	spin_unlock(&syncobj->lock);
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci/**
31962306a36Sopenharmony_ci * drm_syncobj_add_point - add new timeline point to the syncobj
32062306a36Sopenharmony_ci * @syncobj: sync object to add timeline point do
32162306a36Sopenharmony_ci * @chain: chain node to use to add the point
32262306a36Sopenharmony_ci * @fence: fence to encapsulate in the chain node
32362306a36Sopenharmony_ci * @point: sequence number to use for the point
32462306a36Sopenharmony_ci *
32562306a36Sopenharmony_ci * Add the chain node as new timeline point to the syncobj.
32662306a36Sopenharmony_ci */
32762306a36Sopenharmony_civoid drm_syncobj_add_point(struct drm_syncobj *syncobj,
32862306a36Sopenharmony_ci			   struct dma_fence_chain *chain,
32962306a36Sopenharmony_ci			   struct dma_fence *fence,
33062306a36Sopenharmony_ci			   uint64_t point)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct syncobj_wait_entry *wait_cur, *wait_tmp;
33362306a36Sopenharmony_ci	struct syncobj_eventfd_entry *ev_fd_cur, *ev_fd_tmp;
33462306a36Sopenharmony_ci	struct dma_fence *prev;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	dma_fence_get(fence);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	spin_lock(&syncobj->lock);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	prev = drm_syncobj_fence_get(syncobj);
34162306a36Sopenharmony_ci	/* You are adding an unorder point to timeline, which could cause payload returned from query_ioctl is 0! */
34262306a36Sopenharmony_ci	if (prev && prev->seqno >= point)
34362306a36Sopenharmony_ci		DRM_DEBUG("You are adding an unorder point to timeline!\n");
34462306a36Sopenharmony_ci	dma_fence_chain_init(chain, prev, fence, point);
34562306a36Sopenharmony_ci	rcu_assign_pointer(syncobj->fence, &chain->base);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	list_for_each_entry_safe(wait_cur, wait_tmp, &syncobj->cb_list, node)
34862306a36Sopenharmony_ci		syncobj_wait_syncobj_func(syncobj, wait_cur);
34962306a36Sopenharmony_ci	list_for_each_entry_safe(ev_fd_cur, ev_fd_tmp, &syncobj->ev_fd_list, node)
35062306a36Sopenharmony_ci		syncobj_eventfd_entry_func(syncobj, ev_fd_cur);
35162306a36Sopenharmony_ci	spin_unlock(&syncobj->lock);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	/* Walk the chain once to trigger garbage collection */
35462306a36Sopenharmony_ci	dma_fence_chain_for_each(fence, prev);
35562306a36Sopenharmony_ci	dma_fence_put(prev);
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_syncobj_add_point);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci/**
36062306a36Sopenharmony_ci * drm_syncobj_replace_fence - replace fence in a sync object.
36162306a36Sopenharmony_ci * @syncobj: Sync object to replace fence in
36262306a36Sopenharmony_ci * @fence: fence to install in sync file.
36362306a36Sopenharmony_ci *
36462306a36Sopenharmony_ci * This replaces the fence on a sync object.
36562306a36Sopenharmony_ci */
36662306a36Sopenharmony_civoid drm_syncobj_replace_fence(struct drm_syncobj *syncobj,
36762306a36Sopenharmony_ci			       struct dma_fence *fence)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct dma_fence *old_fence;
37062306a36Sopenharmony_ci	struct syncobj_wait_entry *wait_cur, *wait_tmp;
37162306a36Sopenharmony_ci	struct syncobj_eventfd_entry *ev_fd_cur, *ev_fd_tmp;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (fence)
37462306a36Sopenharmony_ci		dma_fence_get(fence);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	spin_lock(&syncobj->lock);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	old_fence = rcu_dereference_protected(syncobj->fence,
37962306a36Sopenharmony_ci					      lockdep_is_held(&syncobj->lock));
38062306a36Sopenharmony_ci	rcu_assign_pointer(syncobj->fence, fence);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if (fence != old_fence) {
38362306a36Sopenharmony_ci		list_for_each_entry_safe(wait_cur, wait_tmp, &syncobj->cb_list, node)
38462306a36Sopenharmony_ci			syncobj_wait_syncobj_func(syncobj, wait_cur);
38562306a36Sopenharmony_ci		list_for_each_entry_safe(ev_fd_cur, ev_fd_tmp, &syncobj->ev_fd_list, node)
38662306a36Sopenharmony_ci			syncobj_eventfd_entry_func(syncobj, ev_fd_cur);
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	spin_unlock(&syncobj->lock);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	dma_fence_put(old_fence);
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_syncobj_replace_fence);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci/**
39662306a36Sopenharmony_ci * drm_syncobj_assign_null_handle - assign a stub fence to the sync object
39762306a36Sopenharmony_ci * @syncobj: sync object to assign the fence on
39862306a36Sopenharmony_ci *
39962306a36Sopenharmony_ci * Assign a already signaled stub fence to the sync object.
40062306a36Sopenharmony_ci */
40162306a36Sopenharmony_cistatic int drm_syncobj_assign_null_handle(struct drm_syncobj *syncobj)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	struct dma_fence *fence = dma_fence_allocate_private_stub(ktime_get());
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (!fence)
40662306a36Sopenharmony_ci		return -ENOMEM;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	drm_syncobj_replace_fence(syncobj, fence);
40962306a36Sopenharmony_ci	dma_fence_put(fence);
41062306a36Sopenharmony_ci	return 0;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci/* 5s default for wait submission */
41462306a36Sopenharmony_ci#define DRM_SYNCOBJ_WAIT_FOR_SUBMIT_TIMEOUT 5000000000ULL
41562306a36Sopenharmony_ci/**
41662306a36Sopenharmony_ci * drm_syncobj_find_fence - lookup and reference the fence in a sync object
41762306a36Sopenharmony_ci * @file_private: drm file private pointer
41862306a36Sopenharmony_ci * @handle: sync object handle to lookup.
41962306a36Sopenharmony_ci * @point: timeline point
42062306a36Sopenharmony_ci * @flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or not
42162306a36Sopenharmony_ci * @fence: out parameter for the fence
42262306a36Sopenharmony_ci *
42362306a36Sopenharmony_ci * This is just a convenience function that combines drm_syncobj_find() and
42462306a36Sopenharmony_ci * drm_syncobj_fence_get().
42562306a36Sopenharmony_ci *
42662306a36Sopenharmony_ci * Returns 0 on success or a negative error value on failure. On success @fence
42762306a36Sopenharmony_ci * contains a reference to the fence, which must be released by calling
42862306a36Sopenharmony_ci * dma_fence_put().
42962306a36Sopenharmony_ci */
43062306a36Sopenharmony_ciint drm_syncobj_find_fence(struct drm_file *file_private,
43162306a36Sopenharmony_ci			   u32 handle, u64 point, u64 flags,
43262306a36Sopenharmony_ci			   struct dma_fence **fence)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);
43562306a36Sopenharmony_ci	struct syncobj_wait_entry wait;
43662306a36Sopenharmony_ci	u64 timeout = nsecs_to_jiffies64(DRM_SYNCOBJ_WAIT_FOR_SUBMIT_TIMEOUT);
43762306a36Sopenharmony_ci	int ret;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	if (!syncobj)
44062306a36Sopenharmony_ci		return -ENOENT;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	/* Waiting for userspace with locks help is illegal cause that can
44362306a36Sopenharmony_ci	 * trivial deadlock with page faults for example. Make lockdep complain
44462306a36Sopenharmony_ci	 * about it early on.
44562306a36Sopenharmony_ci	 */
44662306a36Sopenharmony_ci	if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) {
44762306a36Sopenharmony_ci		might_sleep();
44862306a36Sopenharmony_ci		lockdep_assert_none_held_once();
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	*fence = drm_syncobj_fence_get(syncobj);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	if (*fence) {
45462306a36Sopenharmony_ci		ret = dma_fence_chain_find_seqno(fence, point);
45562306a36Sopenharmony_ci		if (!ret) {
45662306a36Sopenharmony_ci			/* If the requested seqno is already signaled
45762306a36Sopenharmony_ci			 * drm_syncobj_find_fence may return a NULL
45862306a36Sopenharmony_ci			 * fence. To make sure the recipient gets
45962306a36Sopenharmony_ci			 * signalled, use a new fence instead.
46062306a36Sopenharmony_ci			 */
46162306a36Sopenharmony_ci			if (!*fence)
46262306a36Sopenharmony_ci				*fence = dma_fence_get_stub();
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci			goto out;
46562306a36Sopenharmony_ci		}
46662306a36Sopenharmony_ci		dma_fence_put(*fence);
46762306a36Sopenharmony_ci	} else {
46862306a36Sopenharmony_ci		ret = -EINVAL;
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	if (!(flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT))
47262306a36Sopenharmony_ci		goto out;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	memset(&wait, 0, sizeof(wait));
47562306a36Sopenharmony_ci	wait.task = current;
47662306a36Sopenharmony_ci	wait.point = point;
47762306a36Sopenharmony_ci	drm_syncobj_fence_add_wait(syncobj, &wait);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	do {
48062306a36Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
48162306a36Sopenharmony_ci		if (wait.fence) {
48262306a36Sopenharmony_ci			ret = 0;
48362306a36Sopenharmony_ci			break;
48462306a36Sopenharmony_ci		}
48562306a36Sopenharmony_ci                if (timeout == 0) {
48662306a36Sopenharmony_ci                        ret = -ETIME;
48762306a36Sopenharmony_ci                        break;
48862306a36Sopenharmony_ci                }
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci		if (signal_pending(current)) {
49162306a36Sopenharmony_ci			ret = -ERESTARTSYS;
49262306a36Sopenharmony_ci			break;
49362306a36Sopenharmony_ci		}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci                timeout = schedule_timeout(timeout);
49662306a36Sopenharmony_ci	} while (1);
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	__set_current_state(TASK_RUNNING);
49962306a36Sopenharmony_ci	*fence = wait.fence;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	if (wait.node.next)
50262306a36Sopenharmony_ci		drm_syncobj_remove_wait(syncobj, &wait);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ciout:
50562306a36Sopenharmony_ci	drm_syncobj_put(syncobj);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	return ret;
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ciEXPORT_SYMBOL(drm_syncobj_find_fence);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci/**
51262306a36Sopenharmony_ci * drm_syncobj_free - free a sync object.
51362306a36Sopenharmony_ci * @kref: kref to free.
51462306a36Sopenharmony_ci *
51562306a36Sopenharmony_ci * Only to be called from kref_put in drm_syncobj_put.
51662306a36Sopenharmony_ci */
51762306a36Sopenharmony_civoid drm_syncobj_free(struct kref *kref)
51862306a36Sopenharmony_ci{
51962306a36Sopenharmony_ci	struct drm_syncobj *syncobj = container_of(kref,
52062306a36Sopenharmony_ci						   struct drm_syncobj,
52162306a36Sopenharmony_ci						   refcount);
52262306a36Sopenharmony_ci	struct syncobj_eventfd_entry *ev_fd_cur, *ev_fd_tmp;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	drm_syncobj_replace_fence(syncobj, NULL);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	list_for_each_entry_safe(ev_fd_cur, ev_fd_tmp, &syncobj->ev_fd_list, node)
52762306a36Sopenharmony_ci		syncobj_eventfd_entry_free(ev_fd_cur);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	kfree(syncobj);
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_syncobj_free);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci/**
53462306a36Sopenharmony_ci * drm_syncobj_create - create a new syncobj
53562306a36Sopenharmony_ci * @out_syncobj: returned syncobj
53662306a36Sopenharmony_ci * @flags: DRM_SYNCOBJ_* flags
53762306a36Sopenharmony_ci * @fence: if non-NULL, the syncobj will represent this fence
53862306a36Sopenharmony_ci *
53962306a36Sopenharmony_ci * This is the first function to create a sync object. After creating, drivers
54062306a36Sopenharmony_ci * probably want to make it available to userspace, either through
54162306a36Sopenharmony_ci * drm_syncobj_get_handle() or drm_syncobj_get_fd().
54262306a36Sopenharmony_ci *
54362306a36Sopenharmony_ci * Returns 0 on success or a negative error value on failure.
54462306a36Sopenharmony_ci */
54562306a36Sopenharmony_ciint drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags,
54662306a36Sopenharmony_ci		       struct dma_fence *fence)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	int ret;
54962306a36Sopenharmony_ci	struct drm_syncobj *syncobj;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	syncobj = kzalloc(sizeof(struct drm_syncobj), GFP_KERNEL);
55262306a36Sopenharmony_ci	if (!syncobj)
55362306a36Sopenharmony_ci		return -ENOMEM;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	kref_init(&syncobj->refcount);
55662306a36Sopenharmony_ci	INIT_LIST_HEAD(&syncobj->cb_list);
55762306a36Sopenharmony_ci	INIT_LIST_HEAD(&syncobj->ev_fd_list);
55862306a36Sopenharmony_ci	spin_lock_init(&syncobj->lock);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	if (flags & DRM_SYNCOBJ_CREATE_SIGNALED) {
56162306a36Sopenharmony_ci		ret = drm_syncobj_assign_null_handle(syncobj);
56262306a36Sopenharmony_ci		if (ret < 0) {
56362306a36Sopenharmony_ci			drm_syncobj_put(syncobj);
56462306a36Sopenharmony_ci			return ret;
56562306a36Sopenharmony_ci		}
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (fence)
56962306a36Sopenharmony_ci		drm_syncobj_replace_fence(syncobj, fence);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	*out_syncobj = syncobj;
57262306a36Sopenharmony_ci	return 0;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_syncobj_create);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci/**
57762306a36Sopenharmony_ci * drm_syncobj_get_handle - get a handle from a syncobj
57862306a36Sopenharmony_ci * @file_private: drm file private pointer
57962306a36Sopenharmony_ci * @syncobj: Sync object to export
58062306a36Sopenharmony_ci * @handle: out parameter with the new handle
58162306a36Sopenharmony_ci *
58262306a36Sopenharmony_ci * Exports a sync object created with drm_syncobj_create() as a handle on
58362306a36Sopenharmony_ci * @file_private to userspace.
58462306a36Sopenharmony_ci *
58562306a36Sopenharmony_ci * Returns 0 on success or a negative error value on failure.
58662306a36Sopenharmony_ci */
58762306a36Sopenharmony_ciint drm_syncobj_get_handle(struct drm_file *file_private,
58862306a36Sopenharmony_ci			   struct drm_syncobj *syncobj, u32 *handle)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	int ret;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	/* take a reference to put in the idr */
59362306a36Sopenharmony_ci	drm_syncobj_get(syncobj);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	idr_preload(GFP_KERNEL);
59662306a36Sopenharmony_ci	spin_lock(&file_private->syncobj_table_lock);
59762306a36Sopenharmony_ci	ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT);
59862306a36Sopenharmony_ci	spin_unlock(&file_private->syncobj_table_lock);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	idr_preload_end();
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	if (ret < 0) {
60362306a36Sopenharmony_ci		drm_syncobj_put(syncobj);
60462306a36Sopenharmony_ci		return ret;
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	*handle = ret;
60862306a36Sopenharmony_ci	return 0;
60962306a36Sopenharmony_ci}
61062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_syncobj_get_handle);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic int drm_syncobj_create_as_handle(struct drm_file *file_private,
61362306a36Sopenharmony_ci					u32 *handle, uint32_t flags)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	int ret;
61662306a36Sopenharmony_ci	struct drm_syncobj *syncobj;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	ret = drm_syncobj_create(&syncobj, flags, NULL);
61962306a36Sopenharmony_ci	if (ret)
62062306a36Sopenharmony_ci		return ret;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	ret = drm_syncobj_get_handle(file_private, syncobj, handle);
62362306a36Sopenharmony_ci	drm_syncobj_put(syncobj);
62462306a36Sopenharmony_ci	return ret;
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_cistatic int drm_syncobj_destroy(struct drm_file *file_private,
62862306a36Sopenharmony_ci			       u32 handle)
62962306a36Sopenharmony_ci{
63062306a36Sopenharmony_ci	struct drm_syncobj *syncobj;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	spin_lock(&file_private->syncobj_table_lock);
63362306a36Sopenharmony_ci	syncobj = idr_remove(&file_private->syncobj_idr, handle);
63462306a36Sopenharmony_ci	spin_unlock(&file_private->syncobj_table_lock);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	if (!syncobj)
63762306a36Sopenharmony_ci		return -EINVAL;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	drm_syncobj_put(syncobj);
64062306a36Sopenharmony_ci	return 0;
64162306a36Sopenharmony_ci}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic int drm_syncobj_file_release(struct inode *inode, struct file *file)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	struct drm_syncobj *syncobj = file->private_data;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	drm_syncobj_put(syncobj);
64862306a36Sopenharmony_ci	return 0;
64962306a36Sopenharmony_ci}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_cistatic const struct file_operations drm_syncobj_file_fops = {
65262306a36Sopenharmony_ci	.release = drm_syncobj_file_release,
65362306a36Sopenharmony_ci};
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci/**
65662306a36Sopenharmony_ci * drm_syncobj_get_fd - get a file descriptor from a syncobj
65762306a36Sopenharmony_ci * @syncobj: Sync object to export
65862306a36Sopenharmony_ci * @p_fd: out parameter with the new file descriptor
65962306a36Sopenharmony_ci *
66062306a36Sopenharmony_ci * Exports a sync object created with drm_syncobj_create() as a file descriptor.
66162306a36Sopenharmony_ci *
66262306a36Sopenharmony_ci * Returns 0 on success or a negative error value on failure.
66362306a36Sopenharmony_ci */
66462306a36Sopenharmony_ciint drm_syncobj_get_fd(struct drm_syncobj *syncobj, int *p_fd)
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci	struct file *file;
66762306a36Sopenharmony_ci	int fd;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	fd = get_unused_fd_flags(O_CLOEXEC);
67062306a36Sopenharmony_ci	if (fd < 0)
67162306a36Sopenharmony_ci		return fd;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	file = anon_inode_getfile("syncobj_file",
67462306a36Sopenharmony_ci				  &drm_syncobj_file_fops,
67562306a36Sopenharmony_ci				  syncobj, 0);
67662306a36Sopenharmony_ci	if (IS_ERR(file)) {
67762306a36Sopenharmony_ci		put_unused_fd(fd);
67862306a36Sopenharmony_ci		return PTR_ERR(file);
67962306a36Sopenharmony_ci	}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	drm_syncobj_get(syncobj);
68262306a36Sopenharmony_ci	fd_install(fd, file);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	*p_fd = fd;
68562306a36Sopenharmony_ci	return 0;
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_syncobj_get_fd);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_cistatic int drm_syncobj_handle_to_fd(struct drm_file *file_private,
69062306a36Sopenharmony_ci				    u32 handle, int *p_fd)
69162306a36Sopenharmony_ci{
69262306a36Sopenharmony_ci	struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);
69362306a36Sopenharmony_ci	int ret;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	if (!syncobj)
69662306a36Sopenharmony_ci		return -EINVAL;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	ret = drm_syncobj_get_fd(syncobj, p_fd);
69962306a36Sopenharmony_ci	drm_syncobj_put(syncobj);
70062306a36Sopenharmony_ci	return ret;
70162306a36Sopenharmony_ci}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_cistatic int drm_syncobj_fd_to_handle(struct drm_file *file_private,
70462306a36Sopenharmony_ci				    int fd, u32 *handle)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	struct drm_syncobj *syncobj;
70762306a36Sopenharmony_ci	struct fd f = fdget(fd);
70862306a36Sopenharmony_ci	int ret;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	if (!f.file)
71162306a36Sopenharmony_ci		return -EINVAL;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	if (f.file->f_op != &drm_syncobj_file_fops) {
71462306a36Sopenharmony_ci		fdput(f);
71562306a36Sopenharmony_ci		return -EINVAL;
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	/* take a reference to put in the idr */
71962306a36Sopenharmony_ci	syncobj = f.file->private_data;
72062306a36Sopenharmony_ci	drm_syncobj_get(syncobj);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	idr_preload(GFP_KERNEL);
72362306a36Sopenharmony_ci	spin_lock(&file_private->syncobj_table_lock);
72462306a36Sopenharmony_ci	ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT);
72562306a36Sopenharmony_ci	spin_unlock(&file_private->syncobj_table_lock);
72662306a36Sopenharmony_ci	idr_preload_end();
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	if (ret > 0) {
72962306a36Sopenharmony_ci		*handle = ret;
73062306a36Sopenharmony_ci		ret = 0;
73162306a36Sopenharmony_ci	} else
73262306a36Sopenharmony_ci		drm_syncobj_put(syncobj);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	fdput(f);
73562306a36Sopenharmony_ci	return ret;
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic int drm_syncobj_import_sync_file_fence(struct drm_file *file_private,
73962306a36Sopenharmony_ci					      int fd, int handle)
74062306a36Sopenharmony_ci{
74162306a36Sopenharmony_ci	struct dma_fence *fence = sync_file_get_fence(fd);
74262306a36Sopenharmony_ci	struct drm_syncobj *syncobj;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	if (!fence)
74562306a36Sopenharmony_ci		return -EINVAL;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	syncobj = drm_syncobj_find(file_private, handle);
74862306a36Sopenharmony_ci	if (!syncobj) {
74962306a36Sopenharmony_ci		dma_fence_put(fence);
75062306a36Sopenharmony_ci		return -ENOENT;
75162306a36Sopenharmony_ci	}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	drm_syncobj_replace_fence(syncobj, fence);
75462306a36Sopenharmony_ci	dma_fence_put(fence);
75562306a36Sopenharmony_ci	drm_syncobj_put(syncobj);
75662306a36Sopenharmony_ci	return 0;
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_cistatic int drm_syncobj_export_sync_file(struct drm_file *file_private,
76062306a36Sopenharmony_ci					int handle, int *p_fd)
76162306a36Sopenharmony_ci{
76262306a36Sopenharmony_ci	int ret;
76362306a36Sopenharmony_ci	struct dma_fence *fence;
76462306a36Sopenharmony_ci	struct sync_file *sync_file;
76562306a36Sopenharmony_ci	int fd = get_unused_fd_flags(O_CLOEXEC);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	if (fd < 0)
76862306a36Sopenharmony_ci		return fd;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	ret = drm_syncobj_find_fence(file_private, handle, 0, 0, &fence);
77162306a36Sopenharmony_ci	if (ret)
77262306a36Sopenharmony_ci		goto err_put_fd;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	sync_file = sync_file_create(fence);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	dma_fence_put(fence);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	if (!sync_file) {
77962306a36Sopenharmony_ci		ret = -EINVAL;
78062306a36Sopenharmony_ci		goto err_put_fd;
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	fd_install(fd, sync_file->file);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	*p_fd = fd;
78662306a36Sopenharmony_ci	return 0;
78762306a36Sopenharmony_cierr_put_fd:
78862306a36Sopenharmony_ci	put_unused_fd(fd);
78962306a36Sopenharmony_ci	return ret;
79062306a36Sopenharmony_ci}
79162306a36Sopenharmony_ci/**
79262306a36Sopenharmony_ci * drm_syncobj_open - initializes syncobj file-private structures at devnode open time
79362306a36Sopenharmony_ci * @file_private: drm file-private structure to set up
79462306a36Sopenharmony_ci *
79562306a36Sopenharmony_ci * Called at device open time, sets up the structure for handling refcounting
79662306a36Sopenharmony_ci * of sync objects.
79762306a36Sopenharmony_ci */
79862306a36Sopenharmony_civoid
79962306a36Sopenharmony_cidrm_syncobj_open(struct drm_file *file_private)
80062306a36Sopenharmony_ci{
80162306a36Sopenharmony_ci	idr_init_base(&file_private->syncobj_idr, 1);
80262306a36Sopenharmony_ci	spin_lock_init(&file_private->syncobj_table_lock);
80362306a36Sopenharmony_ci}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_cistatic int
80662306a36Sopenharmony_cidrm_syncobj_release_handle(int id, void *ptr, void *data)
80762306a36Sopenharmony_ci{
80862306a36Sopenharmony_ci	struct drm_syncobj *syncobj = ptr;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	drm_syncobj_put(syncobj);
81162306a36Sopenharmony_ci	return 0;
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci/**
81562306a36Sopenharmony_ci * drm_syncobj_release - release file-private sync object resources
81662306a36Sopenharmony_ci * @file_private: drm file-private structure to clean up
81762306a36Sopenharmony_ci *
81862306a36Sopenharmony_ci * Called at close time when the filp is going away.
81962306a36Sopenharmony_ci *
82062306a36Sopenharmony_ci * Releases any remaining references on objects by this filp.
82162306a36Sopenharmony_ci */
82262306a36Sopenharmony_civoid
82362306a36Sopenharmony_cidrm_syncobj_release(struct drm_file *file_private)
82462306a36Sopenharmony_ci{
82562306a36Sopenharmony_ci	idr_for_each(&file_private->syncobj_idr,
82662306a36Sopenharmony_ci		     &drm_syncobj_release_handle, file_private);
82762306a36Sopenharmony_ci	idr_destroy(&file_private->syncobj_idr);
82862306a36Sopenharmony_ci}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ciint
83162306a36Sopenharmony_cidrm_syncobj_create_ioctl(struct drm_device *dev, void *data,
83262306a36Sopenharmony_ci			 struct drm_file *file_private)
83362306a36Sopenharmony_ci{
83462306a36Sopenharmony_ci	struct drm_syncobj_create *args = data;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
83762306a36Sopenharmony_ci		return -EOPNOTSUPP;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	/* no valid flags yet */
84062306a36Sopenharmony_ci	if (args->flags & ~DRM_SYNCOBJ_CREATE_SIGNALED)
84162306a36Sopenharmony_ci		return -EINVAL;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	return drm_syncobj_create_as_handle(file_private,
84462306a36Sopenharmony_ci					    &args->handle, args->flags);
84562306a36Sopenharmony_ci}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ciint
84862306a36Sopenharmony_cidrm_syncobj_destroy_ioctl(struct drm_device *dev, void *data,
84962306a36Sopenharmony_ci			  struct drm_file *file_private)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	struct drm_syncobj_destroy *args = data;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
85462306a36Sopenharmony_ci		return -EOPNOTSUPP;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	/* make sure padding is empty */
85762306a36Sopenharmony_ci	if (args->pad)
85862306a36Sopenharmony_ci		return -EINVAL;
85962306a36Sopenharmony_ci	return drm_syncobj_destroy(file_private, args->handle);
86062306a36Sopenharmony_ci}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ciint
86362306a36Sopenharmony_cidrm_syncobj_handle_to_fd_ioctl(struct drm_device *dev, void *data,
86462306a36Sopenharmony_ci				   struct drm_file *file_private)
86562306a36Sopenharmony_ci{
86662306a36Sopenharmony_ci	struct drm_syncobj_handle *args = data;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
86962306a36Sopenharmony_ci		return -EOPNOTSUPP;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	if (args->pad)
87262306a36Sopenharmony_ci		return -EINVAL;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	if (args->flags != 0 &&
87562306a36Sopenharmony_ci	    args->flags != DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE)
87662306a36Sopenharmony_ci		return -EINVAL;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	if (args->flags & DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE)
87962306a36Sopenharmony_ci		return drm_syncobj_export_sync_file(file_private, args->handle,
88062306a36Sopenharmony_ci						    &args->fd);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	return drm_syncobj_handle_to_fd(file_private, args->handle,
88362306a36Sopenharmony_ci					&args->fd);
88462306a36Sopenharmony_ci}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ciint
88762306a36Sopenharmony_cidrm_syncobj_fd_to_handle_ioctl(struct drm_device *dev, void *data,
88862306a36Sopenharmony_ci				   struct drm_file *file_private)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	struct drm_syncobj_handle *args = data;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
89362306a36Sopenharmony_ci		return -EOPNOTSUPP;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	if (args->pad)
89662306a36Sopenharmony_ci		return -EINVAL;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	if (args->flags != 0 &&
89962306a36Sopenharmony_ci	    args->flags != DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE)
90062306a36Sopenharmony_ci		return -EINVAL;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	if (args->flags & DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE)
90362306a36Sopenharmony_ci		return drm_syncobj_import_sync_file_fence(file_private,
90462306a36Sopenharmony_ci							  args->fd,
90562306a36Sopenharmony_ci							  args->handle);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	return drm_syncobj_fd_to_handle(file_private, args->fd,
90862306a36Sopenharmony_ci					&args->handle);
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_cistatic int drm_syncobj_transfer_to_timeline(struct drm_file *file_private,
91262306a36Sopenharmony_ci					    struct drm_syncobj_transfer *args)
91362306a36Sopenharmony_ci{
91462306a36Sopenharmony_ci	struct drm_syncobj *timeline_syncobj = NULL;
91562306a36Sopenharmony_ci	struct dma_fence *fence, *tmp;
91662306a36Sopenharmony_ci	struct dma_fence_chain *chain;
91762306a36Sopenharmony_ci	int ret;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	timeline_syncobj = drm_syncobj_find(file_private, args->dst_handle);
92062306a36Sopenharmony_ci	if (!timeline_syncobj) {
92162306a36Sopenharmony_ci		return -ENOENT;
92262306a36Sopenharmony_ci	}
92362306a36Sopenharmony_ci	ret = drm_syncobj_find_fence(file_private, args->src_handle,
92462306a36Sopenharmony_ci				     args->src_point, args->flags,
92562306a36Sopenharmony_ci				     &tmp);
92662306a36Sopenharmony_ci	if (ret)
92762306a36Sopenharmony_ci		goto err_put_timeline;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	fence = dma_fence_unwrap_merge(tmp);
93062306a36Sopenharmony_ci	dma_fence_put(tmp);
93162306a36Sopenharmony_ci	if (!fence) {
93262306a36Sopenharmony_ci		ret = -ENOMEM;
93362306a36Sopenharmony_ci		goto err_put_timeline;
93462306a36Sopenharmony_ci	}
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	chain = dma_fence_chain_alloc();
93762306a36Sopenharmony_ci	if (!chain) {
93862306a36Sopenharmony_ci		ret = -ENOMEM;
93962306a36Sopenharmony_ci		goto err_free_fence;
94062306a36Sopenharmony_ci	}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	drm_syncobj_add_point(timeline_syncobj, chain, fence, args->dst_point);
94362306a36Sopenharmony_cierr_free_fence:
94462306a36Sopenharmony_ci	dma_fence_put(fence);
94562306a36Sopenharmony_cierr_put_timeline:
94662306a36Sopenharmony_ci	drm_syncobj_put(timeline_syncobj);
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	return ret;
94962306a36Sopenharmony_ci}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_cistatic int
95262306a36Sopenharmony_cidrm_syncobj_transfer_to_binary(struct drm_file *file_private,
95362306a36Sopenharmony_ci			       struct drm_syncobj_transfer *args)
95462306a36Sopenharmony_ci{
95562306a36Sopenharmony_ci	struct drm_syncobj *binary_syncobj = NULL;
95662306a36Sopenharmony_ci	struct dma_fence *fence;
95762306a36Sopenharmony_ci	int ret;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	binary_syncobj = drm_syncobj_find(file_private, args->dst_handle);
96062306a36Sopenharmony_ci	if (!binary_syncobj)
96162306a36Sopenharmony_ci		return -ENOENT;
96262306a36Sopenharmony_ci	ret = drm_syncobj_find_fence(file_private, args->src_handle,
96362306a36Sopenharmony_ci				     args->src_point, args->flags, &fence);
96462306a36Sopenharmony_ci	if (ret)
96562306a36Sopenharmony_ci		goto err;
96662306a36Sopenharmony_ci	drm_syncobj_replace_fence(binary_syncobj, fence);
96762306a36Sopenharmony_ci	dma_fence_put(fence);
96862306a36Sopenharmony_cierr:
96962306a36Sopenharmony_ci	drm_syncobj_put(binary_syncobj);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	return ret;
97262306a36Sopenharmony_ci}
97362306a36Sopenharmony_ciint
97462306a36Sopenharmony_cidrm_syncobj_transfer_ioctl(struct drm_device *dev, void *data,
97562306a36Sopenharmony_ci			   struct drm_file *file_private)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	struct drm_syncobj_transfer *args = data;
97862306a36Sopenharmony_ci	int ret;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
98162306a36Sopenharmony_ci		return -EOPNOTSUPP;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	if (args->pad)
98462306a36Sopenharmony_ci		return -EINVAL;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	if (args->dst_point)
98762306a36Sopenharmony_ci		ret = drm_syncobj_transfer_to_timeline(file_private, args);
98862306a36Sopenharmony_ci	else
98962306a36Sopenharmony_ci		ret = drm_syncobj_transfer_to_binary(file_private, args);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	return ret;
99262306a36Sopenharmony_ci}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_cistatic void syncobj_wait_fence_func(struct dma_fence *fence,
99562306a36Sopenharmony_ci				    struct dma_fence_cb *cb)
99662306a36Sopenharmony_ci{
99762306a36Sopenharmony_ci	struct syncobj_wait_entry *wait =
99862306a36Sopenharmony_ci		container_of(cb, struct syncobj_wait_entry, fence_cb);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	wake_up_process(wait->task);
100162306a36Sopenharmony_ci}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_cistatic void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj,
100462306a36Sopenharmony_ci				      struct syncobj_wait_entry *wait)
100562306a36Sopenharmony_ci{
100662306a36Sopenharmony_ci	struct dma_fence *fence;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	/* This happens inside the syncobj lock */
100962306a36Sopenharmony_ci	fence = rcu_dereference_protected(syncobj->fence,
101062306a36Sopenharmony_ci					  lockdep_is_held(&syncobj->lock));
101162306a36Sopenharmony_ci	dma_fence_get(fence);
101262306a36Sopenharmony_ci	if (!fence || dma_fence_chain_find_seqno(&fence, wait->point)) {
101362306a36Sopenharmony_ci		dma_fence_put(fence);
101462306a36Sopenharmony_ci		return;
101562306a36Sopenharmony_ci	} else if (!fence) {
101662306a36Sopenharmony_ci		wait->fence = dma_fence_get_stub();
101762306a36Sopenharmony_ci	} else {
101862306a36Sopenharmony_ci		wait->fence = fence;
101962306a36Sopenharmony_ci	}
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	wake_up_process(wait->task);
102262306a36Sopenharmony_ci	list_del_init(&wait->node);
102362306a36Sopenharmony_ci}
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_cistatic signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs,
102662306a36Sopenharmony_ci						  void __user *user_points,
102762306a36Sopenharmony_ci						  uint32_t count,
102862306a36Sopenharmony_ci						  uint32_t flags,
102962306a36Sopenharmony_ci						  signed long timeout,
103062306a36Sopenharmony_ci						  uint32_t *idx)
103162306a36Sopenharmony_ci{
103262306a36Sopenharmony_ci	struct syncobj_wait_entry *entries;
103362306a36Sopenharmony_ci	struct dma_fence *fence;
103462306a36Sopenharmony_ci	uint64_t *points;
103562306a36Sopenharmony_ci	uint32_t signaled_count, i;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	if (flags & (DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
103862306a36Sopenharmony_ci		     DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE))
103962306a36Sopenharmony_ci		lockdep_assert_none_held_once();
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	points = kmalloc_array(count, sizeof(*points), GFP_KERNEL);
104262306a36Sopenharmony_ci	if (points == NULL)
104362306a36Sopenharmony_ci		return -ENOMEM;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	if (!user_points) {
104662306a36Sopenharmony_ci		memset(points, 0, count * sizeof(uint64_t));
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	} else if (copy_from_user(points, user_points,
104962306a36Sopenharmony_ci				  sizeof(uint64_t) * count)) {
105062306a36Sopenharmony_ci		timeout = -EFAULT;
105162306a36Sopenharmony_ci		goto err_free_points;
105262306a36Sopenharmony_ci	}
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	entries = kcalloc(count, sizeof(*entries), GFP_KERNEL);
105562306a36Sopenharmony_ci	if (!entries) {
105662306a36Sopenharmony_ci		timeout = -ENOMEM;
105762306a36Sopenharmony_ci		goto err_free_points;
105862306a36Sopenharmony_ci	}
105962306a36Sopenharmony_ci	/* Walk the list of sync objects and initialize entries.  We do
106062306a36Sopenharmony_ci	 * this up-front so that we can properly return -EINVAL if there is
106162306a36Sopenharmony_ci	 * a syncobj with a missing fence and then never have the chance of
106262306a36Sopenharmony_ci	 * returning -EINVAL again.
106362306a36Sopenharmony_ci	 */
106462306a36Sopenharmony_ci	signaled_count = 0;
106562306a36Sopenharmony_ci	for (i = 0; i < count; ++i) {
106662306a36Sopenharmony_ci		struct dma_fence *fence;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci		entries[i].task = current;
106962306a36Sopenharmony_ci		entries[i].point = points[i];
107062306a36Sopenharmony_ci		fence = drm_syncobj_fence_get(syncobjs[i]);
107162306a36Sopenharmony_ci		if (!fence || dma_fence_chain_find_seqno(&fence, points[i])) {
107262306a36Sopenharmony_ci			dma_fence_put(fence);
107362306a36Sopenharmony_ci			if (flags & (DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
107462306a36Sopenharmony_ci				     DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE)) {
107562306a36Sopenharmony_ci				continue;
107662306a36Sopenharmony_ci			} else {
107762306a36Sopenharmony_ci				timeout = -EINVAL;
107862306a36Sopenharmony_ci				goto cleanup_entries;
107962306a36Sopenharmony_ci			}
108062306a36Sopenharmony_ci		}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci		if (fence)
108362306a36Sopenharmony_ci			entries[i].fence = fence;
108462306a36Sopenharmony_ci		else
108562306a36Sopenharmony_ci			entries[i].fence = dma_fence_get_stub();
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci		if ((flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE) ||
108862306a36Sopenharmony_ci		    dma_fence_is_signaled(entries[i].fence)) {
108962306a36Sopenharmony_ci			if (signaled_count == 0 && idx)
109062306a36Sopenharmony_ci				*idx = i;
109162306a36Sopenharmony_ci			signaled_count++;
109262306a36Sopenharmony_ci		}
109362306a36Sopenharmony_ci	}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	if (signaled_count == count ||
109662306a36Sopenharmony_ci	    (signaled_count > 0 &&
109762306a36Sopenharmony_ci	     !(flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL)))
109862306a36Sopenharmony_ci		goto cleanup_entries;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	/* There's a very annoying laxness in the dma_fence API here, in
110162306a36Sopenharmony_ci	 * that backends are not required to automatically report when a
110262306a36Sopenharmony_ci	 * fence is signaled prior to fence->ops->enable_signaling() being
110362306a36Sopenharmony_ci	 * called.  So here if we fail to match signaled_count, we need to
110462306a36Sopenharmony_ci	 * fallthough and try a 0 timeout wait!
110562306a36Sopenharmony_ci	 */
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	if (flags & (DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
110862306a36Sopenharmony_ci		     DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE)) {
110962306a36Sopenharmony_ci		for (i = 0; i < count; ++i)
111062306a36Sopenharmony_ci			drm_syncobj_fence_add_wait(syncobjs[i], &entries[i]);
111162306a36Sopenharmony_ci	}
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	do {
111462306a36Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci		signaled_count = 0;
111762306a36Sopenharmony_ci		for (i = 0; i < count; ++i) {
111862306a36Sopenharmony_ci			fence = entries[i].fence;
111962306a36Sopenharmony_ci			if (!fence)
112062306a36Sopenharmony_ci				continue;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci			if ((flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE) ||
112362306a36Sopenharmony_ci			    dma_fence_is_signaled(fence) ||
112462306a36Sopenharmony_ci			    (!entries[i].fence_cb.func &&
112562306a36Sopenharmony_ci			     dma_fence_add_callback(fence,
112662306a36Sopenharmony_ci						    &entries[i].fence_cb,
112762306a36Sopenharmony_ci						    syncobj_wait_fence_func))) {
112862306a36Sopenharmony_ci				/* The fence has been signaled */
112962306a36Sopenharmony_ci				if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL) {
113062306a36Sopenharmony_ci					signaled_count++;
113162306a36Sopenharmony_ci				} else {
113262306a36Sopenharmony_ci					if (idx)
113362306a36Sopenharmony_ci						*idx = i;
113462306a36Sopenharmony_ci					goto done_waiting;
113562306a36Sopenharmony_ci				}
113662306a36Sopenharmony_ci			}
113762306a36Sopenharmony_ci		}
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci		if (signaled_count == count)
114062306a36Sopenharmony_ci			goto done_waiting;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci		if (timeout == 0) {
114362306a36Sopenharmony_ci			timeout = -ETIME;
114462306a36Sopenharmony_ci			goto done_waiting;
114562306a36Sopenharmony_ci		}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci		if (signal_pending(current)) {
114862306a36Sopenharmony_ci			timeout = -ERESTARTSYS;
114962306a36Sopenharmony_ci			goto done_waiting;
115062306a36Sopenharmony_ci		}
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci		timeout = schedule_timeout(timeout);
115362306a36Sopenharmony_ci	} while (1);
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_cidone_waiting:
115662306a36Sopenharmony_ci	__set_current_state(TASK_RUNNING);
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_cicleanup_entries:
115962306a36Sopenharmony_ci	for (i = 0; i < count; ++i) {
116062306a36Sopenharmony_ci		drm_syncobj_remove_wait(syncobjs[i], &entries[i]);
116162306a36Sopenharmony_ci		if (entries[i].fence_cb.func)
116262306a36Sopenharmony_ci			dma_fence_remove_callback(entries[i].fence,
116362306a36Sopenharmony_ci						  &entries[i].fence_cb);
116462306a36Sopenharmony_ci		dma_fence_put(entries[i].fence);
116562306a36Sopenharmony_ci	}
116662306a36Sopenharmony_ci	kfree(entries);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_cierr_free_points:
116962306a36Sopenharmony_ci	kfree(points);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	return timeout;
117262306a36Sopenharmony_ci}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci/**
117562306a36Sopenharmony_ci * drm_timeout_abs_to_jiffies - calculate jiffies timeout from absolute value
117662306a36Sopenharmony_ci *
117762306a36Sopenharmony_ci * @timeout_nsec: timeout nsec component in ns, 0 for poll
117862306a36Sopenharmony_ci *
117962306a36Sopenharmony_ci * Calculate the timeout in jiffies from an absolute time in sec/nsec.
118062306a36Sopenharmony_ci */
118162306a36Sopenharmony_cisigned long drm_timeout_abs_to_jiffies(int64_t timeout_nsec)
118262306a36Sopenharmony_ci{
118362306a36Sopenharmony_ci	ktime_t abs_timeout, now;
118462306a36Sopenharmony_ci	u64 timeout_ns, timeout_jiffies64;
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	/* make 0 timeout means poll - absolute 0 doesn't seem valid */
118762306a36Sopenharmony_ci	if (timeout_nsec == 0)
118862306a36Sopenharmony_ci		return 0;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	abs_timeout = ns_to_ktime(timeout_nsec);
119162306a36Sopenharmony_ci	now = ktime_get();
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	if (!ktime_after(abs_timeout, now))
119462306a36Sopenharmony_ci		return 0;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	timeout_ns = ktime_to_ns(ktime_sub(abs_timeout, now));
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	timeout_jiffies64 = nsecs_to_jiffies64(timeout_ns);
119962306a36Sopenharmony_ci	/*  clamp timeout to avoid infinite timeout */
120062306a36Sopenharmony_ci	if (timeout_jiffies64 >= MAX_SCHEDULE_TIMEOUT - 1)
120162306a36Sopenharmony_ci		return MAX_SCHEDULE_TIMEOUT - 1;
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	return timeout_jiffies64 + 1;
120462306a36Sopenharmony_ci}
120562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_timeout_abs_to_jiffies);
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_cistatic int drm_syncobj_array_wait(struct drm_device *dev,
120862306a36Sopenharmony_ci				  struct drm_file *file_private,
120962306a36Sopenharmony_ci				  struct drm_syncobj_wait *wait,
121062306a36Sopenharmony_ci				  struct drm_syncobj_timeline_wait *timeline_wait,
121162306a36Sopenharmony_ci				  struct drm_syncobj **syncobjs, bool timeline)
121262306a36Sopenharmony_ci{
121362306a36Sopenharmony_ci	signed long timeout = 0;
121462306a36Sopenharmony_ci	uint32_t first = ~0;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	if (!timeline) {
121762306a36Sopenharmony_ci		timeout = drm_timeout_abs_to_jiffies(wait->timeout_nsec);
121862306a36Sopenharmony_ci		timeout = drm_syncobj_array_wait_timeout(syncobjs,
121962306a36Sopenharmony_ci							 NULL,
122062306a36Sopenharmony_ci							 wait->count_handles,
122162306a36Sopenharmony_ci							 wait->flags,
122262306a36Sopenharmony_ci							 timeout, &first);
122362306a36Sopenharmony_ci		if (timeout < 0)
122462306a36Sopenharmony_ci			return timeout;
122562306a36Sopenharmony_ci		wait->first_signaled = first;
122662306a36Sopenharmony_ci	} else {
122762306a36Sopenharmony_ci		timeout = drm_timeout_abs_to_jiffies(timeline_wait->timeout_nsec);
122862306a36Sopenharmony_ci		timeout = drm_syncobj_array_wait_timeout(syncobjs,
122962306a36Sopenharmony_ci							 u64_to_user_ptr(timeline_wait->points),
123062306a36Sopenharmony_ci							 timeline_wait->count_handles,
123162306a36Sopenharmony_ci							 timeline_wait->flags,
123262306a36Sopenharmony_ci							 timeout, &first);
123362306a36Sopenharmony_ci		if (timeout < 0)
123462306a36Sopenharmony_ci			return timeout;
123562306a36Sopenharmony_ci		timeline_wait->first_signaled = first;
123662306a36Sopenharmony_ci	}
123762306a36Sopenharmony_ci	return 0;
123862306a36Sopenharmony_ci}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_cistatic int drm_syncobj_array_find(struct drm_file *file_private,
124162306a36Sopenharmony_ci				  void __user *user_handles,
124262306a36Sopenharmony_ci				  uint32_t count_handles,
124362306a36Sopenharmony_ci				  struct drm_syncobj ***syncobjs_out)
124462306a36Sopenharmony_ci{
124562306a36Sopenharmony_ci	uint32_t i, *handles;
124662306a36Sopenharmony_ci	struct drm_syncobj **syncobjs;
124762306a36Sopenharmony_ci	int ret;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	handles = kmalloc_array(count_handles, sizeof(*handles), GFP_KERNEL);
125062306a36Sopenharmony_ci	if (handles == NULL)
125162306a36Sopenharmony_ci		return -ENOMEM;
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	if (copy_from_user(handles, user_handles,
125462306a36Sopenharmony_ci			   sizeof(uint32_t) * count_handles)) {
125562306a36Sopenharmony_ci		ret = -EFAULT;
125662306a36Sopenharmony_ci		goto err_free_handles;
125762306a36Sopenharmony_ci	}
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	syncobjs = kmalloc_array(count_handles, sizeof(*syncobjs), GFP_KERNEL);
126062306a36Sopenharmony_ci	if (syncobjs == NULL) {
126162306a36Sopenharmony_ci		ret = -ENOMEM;
126262306a36Sopenharmony_ci		goto err_free_handles;
126362306a36Sopenharmony_ci	}
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	for (i = 0; i < count_handles; i++) {
126662306a36Sopenharmony_ci		syncobjs[i] = drm_syncobj_find(file_private, handles[i]);
126762306a36Sopenharmony_ci		if (!syncobjs[i]) {
126862306a36Sopenharmony_ci			ret = -ENOENT;
126962306a36Sopenharmony_ci			goto err_put_syncobjs;
127062306a36Sopenharmony_ci		}
127162306a36Sopenharmony_ci	}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	kfree(handles);
127462306a36Sopenharmony_ci	*syncobjs_out = syncobjs;
127562306a36Sopenharmony_ci	return 0;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_cierr_put_syncobjs:
127862306a36Sopenharmony_ci	while (i-- > 0)
127962306a36Sopenharmony_ci		drm_syncobj_put(syncobjs[i]);
128062306a36Sopenharmony_ci	kfree(syncobjs);
128162306a36Sopenharmony_cierr_free_handles:
128262306a36Sopenharmony_ci	kfree(handles);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	return ret;
128562306a36Sopenharmony_ci}
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_cistatic void drm_syncobj_array_free(struct drm_syncobj **syncobjs,
128862306a36Sopenharmony_ci				   uint32_t count)
128962306a36Sopenharmony_ci{
129062306a36Sopenharmony_ci	uint32_t i;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	for (i = 0; i < count; i++)
129362306a36Sopenharmony_ci		drm_syncobj_put(syncobjs[i]);
129462306a36Sopenharmony_ci	kfree(syncobjs);
129562306a36Sopenharmony_ci}
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ciint
129862306a36Sopenharmony_cidrm_syncobj_wait_ioctl(struct drm_device *dev, void *data,
129962306a36Sopenharmony_ci		       struct drm_file *file_private)
130062306a36Sopenharmony_ci{
130162306a36Sopenharmony_ci	struct drm_syncobj_wait *args = data;
130262306a36Sopenharmony_ci	struct drm_syncobj **syncobjs;
130362306a36Sopenharmony_ci	int ret = 0;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
130662306a36Sopenharmony_ci		return -EOPNOTSUPP;
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	if (args->flags & ~(DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL |
130962306a36Sopenharmony_ci			    DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT))
131062306a36Sopenharmony_ci		return -EINVAL;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	if (args->count_handles == 0)
131362306a36Sopenharmony_ci		return -EINVAL;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	ret = drm_syncobj_array_find(file_private,
131662306a36Sopenharmony_ci				     u64_to_user_ptr(args->handles),
131762306a36Sopenharmony_ci				     args->count_handles,
131862306a36Sopenharmony_ci				     &syncobjs);
131962306a36Sopenharmony_ci	if (ret < 0)
132062306a36Sopenharmony_ci		return ret;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	ret = drm_syncobj_array_wait(dev, file_private,
132362306a36Sopenharmony_ci				     args, NULL, syncobjs, false);
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	drm_syncobj_array_free(syncobjs, args->count_handles);
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	return ret;
132862306a36Sopenharmony_ci}
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ciint
133162306a36Sopenharmony_cidrm_syncobj_timeline_wait_ioctl(struct drm_device *dev, void *data,
133262306a36Sopenharmony_ci				struct drm_file *file_private)
133362306a36Sopenharmony_ci{
133462306a36Sopenharmony_ci	struct drm_syncobj_timeline_wait *args = data;
133562306a36Sopenharmony_ci	struct drm_syncobj **syncobjs;
133662306a36Sopenharmony_ci	int ret = 0;
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
133962306a36Sopenharmony_ci		return -EOPNOTSUPP;
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	if (args->flags & ~(DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL |
134262306a36Sopenharmony_ci			    DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
134362306a36Sopenharmony_ci			    DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE))
134462306a36Sopenharmony_ci		return -EINVAL;
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	if (args->count_handles == 0)
134762306a36Sopenharmony_ci		return -EINVAL;
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	ret = drm_syncobj_array_find(file_private,
135062306a36Sopenharmony_ci				     u64_to_user_ptr(args->handles),
135162306a36Sopenharmony_ci				     args->count_handles,
135262306a36Sopenharmony_ci				     &syncobjs);
135362306a36Sopenharmony_ci	if (ret < 0)
135462306a36Sopenharmony_ci		return ret;
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	ret = drm_syncobj_array_wait(dev, file_private,
135762306a36Sopenharmony_ci				     NULL, args, syncobjs, true);
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	drm_syncobj_array_free(syncobjs, args->count_handles);
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	return ret;
136262306a36Sopenharmony_ci}
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_cistatic void syncobj_eventfd_entry_fence_func(struct dma_fence *fence,
136562306a36Sopenharmony_ci					     struct dma_fence_cb *cb)
136662306a36Sopenharmony_ci{
136762306a36Sopenharmony_ci	struct syncobj_eventfd_entry *entry =
136862306a36Sopenharmony_ci		container_of(cb, struct syncobj_eventfd_entry, fence_cb);
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	eventfd_signal(entry->ev_fd_ctx, 1);
137162306a36Sopenharmony_ci	syncobj_eventfd_entry_free(entry);
137262306a36Sopenharmony_ci}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_cistatic void
137562306a36Sopenharmony_cisyncobj_eventfd_entry_func(struct drm_syncobj *syncobj,
137662306a36Sopenharmony_ci			   struct syncobj_eventfd_entry *entry)
137762306a36Sopenharmony_ci{
137862306a36Sopenharmony_ci	int ret;
137962306a36Sopenharmony_ci	struct dma_fence *fence;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	/* This happens inside the syncobj lock */
138262306a36Sopenharmony_ci	fence = dma_fence_get(rcu_dereference_protected(syncobj->fence, 1));
138362306a36Sopenharmony_ci	if (!fence)
138462306a36Sopenharmony_ci		return;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	ret = dma_fence_chain_find_seqno(&fence, entry->point);
138762306a36Sopenharmony_ci	if (ret != 0) {
138862306a36Sopenharmony_ci		/* The given seqno has not been submitted yet. */
138962306a36Sopenharmony_ci		dma_fence_put(fence);
139062306a36Sopenharmony_ci		return;
139162306a36Sopenharmony_ci	} else if (!fence) {
139262306a36Sopenharmony_ci		/* If dma_fence_chain_find_seqno returns 0 but sets the fence
139362306a36Sopenharmony_ci		 * to NULL, it implies that the given seqno is signaled and a
139462306a36Sopenharmony_ci		 * later seqno has already been submitted. Assign a stub fence
139562306a36Sopenharmony_ci		 * so that the eventfd still gets signaled below.
139662306a36Sopenharmony_ci		 */
139762306a36Sopenharmony_ci		fence = dma_fence_get_stub();
139862306a36Sopenharmony_ci	}
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	list_del_init(&entry->node);
140162306a36Sopenharmony_ci	entry->fence = fence;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	if (entry->flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE) {
140462306a36Sopenharmony_ci		eventfd_signal(entry->ev_fd_ctx, 1);
140562306a36Sopenharmony_ci		syncobj_eventfd_entry_free(entry);
140662306a36Sopenharmony_ci	} else {
140762306a36Sopenharmony_ci		ret = dma_fence_add_callback(fence, &entry->fence_cb,
140862306a36Sopenharmony_ci					     syncobj_eventfd_entry_fence_func);
140962306a36Sopenharmony_ci		if (ret == -ENOENT) {
141062306a36Sopenharmony_ci			eventfd_signal(entry->ev_fd_ctx, 1);
141162306a36Sopenharmony_ci			syncobj_eventfd_entry_free(entry);
141262306a36Sopenharmony_ci		}
141362306a36Sopenharmony_ci	}
141462306a36Sopenharmony_ci}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ciint
141762306a36Sopenharmony_cidrm_syncobj_eventfd_ioctl(struct drm_device *dev, void *data,
141862306a36Sopenharmony_ci			  struct drm_file *file_private)
141962306a36Sopenharmony_ci{
142062306a36Sopenharmony_ci	struct drm_syncobj_eventfd *args = data;
142162306a36Sopenharmony_ci	struct drm_syncobj *syncobj;
142262306a36Sopenharmony_ci	struct eventfd_ctx *ev_fd_ctx;
142362306a36Sopenharmony_ci	struct syncobj_eventfd_entry *entry;
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
142662306a36Sopenharmony_ci		return -EOPNOTSUPP;
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	if (args->flags & ~DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE)
142962306a36Sopenharmony_ci		return -EINVAL;
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	if (args->pad)
143262306a36Sopenharmony_ci		return -EINVAL;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	syncobj = drm_syncobj_find(file_private, args->handle);
143562306a36Sopenharmony_ci	if (!syncobj)
143662306a36Sopenharmony_ci		return -ENOENT;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	ev_fd_ctx = eventfd_ctx_fdget(args->fd);
143962306a36Sopenharmony_ci	if (IS_ERR(ev_fd_ctx))
144062306a36Sopenharmony_ci		return PTR_ERR(ev_fd_ctx);
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
144362306a36Sopenharmony_ci	if (!entry) {
144462306a36Sopenharmony_ci		eventfd_ctx_put(ev_fd_ctx);
144562306a36Sopenharmony_ci		return -ENOMEM;
144662306a36Sopenharmony_ci	}
144762306a36Sopenharmony_ci	entry->syncobj = syncobj;
144862306a36Sopenharmony_ci	entry->ev_fd_ctx = ev_fd_ctx;
144962306a36Sopenharmony_ci	entry->point = args->point;
145062306a36Sopenharmony_ci	entry->flags = args->flags;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	drm_syncobj_add_eventfd(syncobj, entry);
145362306a36Sopenharmony_ci	drm_syncobj_put(syncobj);
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	return 0;
145662306a36Sopenharmony_ci}
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ciint
145962306a36Sopenharmony_cidrm_syncobj_reset_ioctl(struct drm_device *dev, void *data,
146062306a36Sopenharmony_ci			struct drm_file *file_private)
146162306a36Sopenharmony_ci{
146262306a36Sopenharmony_ci	struct drm_syncobj_array *args = data;
146362306a36Sopenharmony_ci	struct drm_syncobj **syncobjs;
146462306a36Sopenharmony_ci	uint32_t i;
146562306a36Sopenharmony_ci	int ret;
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
146862306a36Sopenharmony_ci		return -EOPNOTSUPP;
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	if (args->pad != 0)
147162306a36Sopenharmony_ci		return -EINVAL;
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	if (args->count_handles == 0)
147462306a36Sopenharmony_ci		return -EINVAL;
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	ret = drm_syncobj_array_find(file_private,
147762306a36Sopenharmony_ci				     u64_to_user_ptr(args->handles),
147862306a36Sopenharmony_ci				     args->count_handles,
147962306a36Sopenharmony_ci				     &syncobjs);
148062306a36Sopenharmony_ci	if (ret < 0)
148162306a36Sopenharmony_ci		return ret;
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	for (i = 0; i < args->count_handles; i++)
148462306a36Sopenharmony_ci		drm_syncobj_replace_fence(syncobjs[i], NULL);
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	drm_syncobj_array_free(syncobjs, args->count_handles);
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	return 0;
148962306a36Sopenharmony_ci}
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ciint
149262306a36Sopenharmony_cidrm_syncobj_signal_ioctl(struct drm_device *dev, void *data,
149362306a36Sopenharmony_ci			 struct drm_file *file_private)
149462306a36Sopenharmony_ci{
149562306a36Sopenharmony_ci	struct drm_syncobj_array *args = data;
149662306a36Sopenharmony_ci	struct drm_syncobj **syncobjs;
149762306a36Sopenharmony_ci	uint32_t i;
149862306a36Sopenharmony_ci	int ret;
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
150162306a36Sopenharmony_ci		return -EOPNOTSUPP;
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	if (args->pad != 0)
150462306a36Sopenharmony_ci		return -EINVAL;
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	if (args->count_handles == 0)
150762306a36Sopenharmony_ci		return -EINVAL;
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	ret = drm_syncobj_array_find(file_private,
151062306a36Sopenharmony_ci				     u64_to_user_ptr(args->handles),
151162306a36Sopenharmony_ci				     args->count_handles,
151262306a36Sopenharmony_ci				     &syncobjs);
151362306a36Sopenharmony_ci	if (ret < 0)
151462306a36Sopenharmony_ci		return ret;
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	for (i = 0; i < args->count_handles; i++) {
151762306a36Sopenharmony_ci		ret = drm_syncobj_assign_null_handle(syncobjs[i]);
151862306a36Sopenharmony_ci		if (ret < 0)
151962306a36Sopenharmony_ci			break;
152062306a36Sopenharmony_ci	}
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	drm_syncobj_array_free(syncobjs, args->count_handles);
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	return ret;
152562306a36Sopenharmony_ci}
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ciint
152862306a36Sopenharmony_cidrm_syncobj_timeline_signal_ioctl(struct drm_device *dev, void *data,
152962306a36Sopenharmony_ci				  struct drm_file *file_private)
153062306a36Sopenharmony_ci{
153162306a36Sopenharmony_ci	struct drm_syncobj_timeline_array *args = data;
153262306a36Sopenharmony_ci	struct drm_syncobj **syncobjs;
153362306a36Sopenharmony_ci	struct dma_fence_chain **chains;
153462306a36Sopenharmony_ci	uint64_t *points;
153562306a36Sopenharmony_ci	uint32_t i, j;
153662306a36Sopenharmony_ci	int ret;
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
153962306a36Sopenharmony_ci		return -EOPNOTSUPP;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	if (args->flags != 0)
154262306a36Sopenharmony_ci		return -EINVAL;
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	if (args->count_handles == 0)
154562306a36Sopenharmony_ci		return -EINVAL;
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	ret = drm_syncobj_array_find(file_private,
154862306a36Sopenharmony_ci				     u64_to_user_ptr(args->handles),
154962306a36Sopenharmony_ci				     args->count_handles,
155062306a36Sopenharmony_ci				     &syncobjs);
155162306a36Sopenharmony_ci	if (ret < 0)
155262306a36Sopenharmony_ci		return ret;
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	points = kmalloc_array(args->count_handles, sizeof(*points),
155562306a36Sopenharmony_ci			       GFP_KERNEL);
155662306a36Sopenharmony_ci	if (!points) {
155762306a36Sopenharmony_ci		ret = -ENOMEM;
155862306a36Sopenharmony_ci		goto out;
155962306a36Sopenharmony_ci	}
156062306a36Sopenharmony_ci	if (!u64_to_user_ptr(args->points)) {
156162306a36Sopenharmony_ci		memset(points, 0, args->count_handles * sizeof(uint64_t));
156262306a36Sopenharmony_ci	} else if (copy_from_user(points, u64_to_user_ptr(args->points),
156362306a36Sopenharmony_ci				  sizeof(uint64_t) * args->count_handles)) {
156462306a36Sopenharmony_ci		ret = -EFAULT;
156562306a36Sopenharmony_ci		goto err_points;
156662306a36Sopenharmony_ci	}
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	chains = kmalloc_array(args->count_handles, sizeof(void *), GFP_KERNEL);
156962306a36Sopenharmony_ci	if (!chains) {
157062306a36Sopenharmony_ci		ret = -ENOMEM;
157162306a36Sopenharmony_ci		goto err_points;
157262306a36Sopenharmony_ci	}
157362306a36Sopenharmony_ci	for (i = 0; i < args->count_handles; i++) {
157462306a36Sopenharmony_ci		chains[i] = dma_fence_chain_alloc();
157562306a36Sopenharmony_ci		if (!chains[i]) {
157662306a36Sopenharmony_ci			for (j = 0; j < i; j++)
157762306a36Sopenharmony_ci				dma_fence_chain_free(chains[j]);
157862306a36Sopenharmony_ci			ret = -ENOMEM;
157962306a36Sopenharmony_ci			goto err_chains;
158062306a36Sopenharmony_ci		}
158162306a36Sopenharmony_ci	}
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	for (i = 0; i < args->count_handles; i++) {
158462306a36Sopenharmony_ci		struct dma_fence *fence = dma_fence_get_stub();
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci		drm_syncobj_add_point(syncobjs[i], chains[i],
158762306a36Sopenharmony_ci				      fence, points[i]);
158862306a36Sopenharmony_ci		dma_fence_put(fence);
158962306a36Sopenharmony_ci	}
159062306a36Sopenharmony_cierr_chains:
159162306a36Sopenharmony_ci	kfree(chains);
159262306a36Sopenharmony_cierr_points:
159362306a36Sopenharmony_ci	kfree(points);
159462306a36Sopenharmony_ciout:
159562306a36Sopenharmony_ci	drm_syncobj_array_free(syncobjs, args->count_handles);
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	return ret;
159862306a36Sopenharmony_ci}
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ciint drm_syncobj_query_ioctl(struct drm_device *dev, void *data,
160162306a36Sopenharmony_ci			    struct drm_file *file_private)
160262306a36Sopenharmony_ci{
160362306a36Sopenharmony_ci	struct drm_syncobj_timeline_array *args = data;
160462306a36Sopenharmony_ci	struct drm_syncobj **syncobjs;
160562306a36Sopenharmony_ci	uint64_t __user *points = u64_to_user_ptr(args->points);
160662306a36Sopenharmony_ci	uint32_t i;
160762306a36Sopenharmony_ci	int ret;
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
161062306a36Sopenharmony_ci		return -EOPNOTSUPP;
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	if (args->flags & ~DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED)
161362306a36Sopenharmony_ci		return -EINVAL;
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci	if (args->count_handles == 0)
161662306a36Sopenharmony_ci		return -EINVAL;
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	ret = drm_syncobj_array_find(file_private,
161962306a36Sopenharmony_ci				     u64_to_user_ptr(args->handles),
162062306a36Sopenharmony_ci				     args->count_handles,
162162306a36Sopenharmony_ci				     &syncobjs);
162262306a36Sopenharmony_ci	if (ret < 0)
162362306a36Sopenharmony_ci		return ret;
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	for (i = 0; i < args->count_handles; i++) {
162662306a36Sopenharmony_ci		struct dma_fence_chain *chain;
162762306a36Sopenharmony_ci		struct dma_fence *fence;
162862306a36Sopenharmony_ci		uint64_t point;
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci		fence = drm_syncobj_fence_get(syncobjs[i]);
163162306a36Sopenharmony_ci		chain = to_dma_fence_chain(fence);
163262306a36Sopenharmony_ci		if (chain) {
163362306a36Sopenharmony_ci			struct dma_fence *iter, *last_signaled =
163462306a36Sopenharmony_ci				dma_fence_get(fence);
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci			if (args->flags &
163762306a36Sopenharmony_ci			    DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED) {
163862306a36Sopenharmony_ci				point = fence->seqno;
163962306a36Sopenharmony_ci			} else {
164062306a36Sopenharmony_ci				dma_fence_chain_for_each(iter, fence) {
164162306a36Sopenharmony_ci					if (iter->context != fence->context) {
164262306a36Sopenharmony_ci						dma_fence_put(iter);
164362306a36Sopenharmony_ci						/* It is most likely that timeline has
164462306a36Sopenharmony_ci						* unorder points. */
164562306a36Sopenharmony_ci						break;
164662306a36Sopenharmony_ci					}
164762306a36Sopenharmony_ci					dma_fence_put(last_signaled);
164862306a36Sopenharmony_ci					last_signaled = dma_fence_get(iter);
164962306a36Sopenharmony_ci				}
165062306a36Sopenharmony_ci				point = dma_fence_is_signaled(last_signaled) ?
165162306a36Sopenharmony_ci					last_signaled->seqno :
165262306a36Sopenharmony_ci					to_dma_fence_chain(last_signaled)->prev_seqno;
165362306a36Sopenharmony_ci			}
165462306a36Sopenharmony_ci			dma_fence_put(last_signaled);
165562306a36Sopenharmony_ci		} else {
165662306a36Sopenharmony_ci			point = 0;
165762306a36Sopenharmony_ci		}
165862306a36Sopenharmony_ci		dma_fence_put(fence);
165962306a36Sopenharmony_ci		ret = copy_to_user(&points[i], &point, sizeof(uint64_t));
166062306a36Sopenharmony_ci		ret = ret ? -EFAULT : 0;
166162306a36Sopenharmony_ci		if (ret)
166262306a36Sopenharmony_ci			break;
166362306a36Sopenharmony_ci	}
166462306a36Sopenharmony_ci	drm_syncobj_array_free(syncobjs, args->count_handles);
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	return ret;
166762306a36Sopenharmony_ci}
1668