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