18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright 2017 Red Hat 38c2ecf20Sopenharmony_ci * Parts ported from amdgpu (fence wait code). 48c2ecf20Sopenharmony_ci * Copyright 2016 Advanced Micro Devices, Inc. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 78c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 88c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 98c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 108c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 118c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next 148c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 158c2ecf20Sopenharmony_ci * Software. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 188c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 198c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 208c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 218c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 228c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 238c2ecf20Sopenharmony_ci * IN THE SOFTWARE. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * Authors: 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/** 308c2ecf20Sopenharmony_ci * DOC: Overview 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * DRM synchronisation objects (syncobj, see struct &drm_syncobj) provide a 338c2ecf20Sopenharmony_ci * container for a synchronization primitive which can be used by userspace 348c2ecf20Sopenharmony_ci * to explicitly synchronize GPU commands, can be shared between userspace 358c2ecf20Sopenharmony_ci * processes, and can be shared between different DRM drivers. 368c2ecf20Sopenharmony_ci * Their primary use-case is to implement Vulkan fences and semaphores. 378c2ecf20Sopenharmony_ci * The syncobj userspace API provides ioctls for several operations: 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * - Creation and destruction of syncobjs 408c2ecf20Sopenharmony_ci * - Import and export of syncobjs to/from a syncobj file descriptor 418c2ecf20Sopenharmony_ci * - Import and export a syncobj's underlying fence to/from a sync file 428c2ecf20Sopenharmony_ci * - Reset a syncobj (set its fence to NULL) 438c2ecf20Sopenharmony_ci * - Signal a syncobj (set a trivially signaled fence) 448c2ecf20Sopenharmony_ci * - Wait for a syncobj's fence to appear and be signaled 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * The syncobj userspace API also provides operations to manipulate a syncobj 478c2ecf20Sopenharmony_ci * in terms of a timeline of struct &dma_fence_chain rather than a single 488c2ecf20Sopenharmony_ci * struct &dma_fence, through the following operations: 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * - Signal a given point on the timeline 518c2ecf20Sopenharmony_ci * - Wait for a given point to appear and/or be signaled 528c2ecf20Sopenharmony_ci * - Import and export from/to a given point of a timeline 538c2ecf20Sopenharmony_ci * 548c2ecf20Sopenharmony_ci * At it's core, a syncobj is simply a wrapper around a pointer to a struct 558c2ecf20Sopenharmony_ci * &dma_fence which may be NULL. 568c2ecf20Sopenharmony_ci * When a syncobj is first created, its pointer is either NULL or a pointer 578c2ecf20Sopenharmony_ci * to an already signaled fence depending on whether the 588c2ecf20Sopenharmony_ci * &DRM_SYNCOBJ_CREATE_SIGNALED flag is passed to 598c2ecf20Sopenharmony_ci * &DRM_IOCTL_SYNCOBJ_CREATE. 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * If the syncobj is considered as a binary (its state is either signaled or 628c2ecf20Sopenharmony_ci * unsignaled) primitive, when GPU work is enqueued in a DRM driver to signal 638c2ecf20Sopenharmony_ci * the syncobj, the syncobj's fence is replaced with a fence which will be 648c2ecf20Sopenharmony_ci * signaled by the completion of that work. 658c2ecf20Sopenharmony_ci * If the syncobj is considered as a timeline primitive, when GPU work is 668c2ecf20Sopenharmony_ci * enqueued in a DRM driver to signal the a given point of the syncobj, a new 678c2ecf20Sopenharmony_ci * struct &dma_fence_chain pointing to the DRM driver's fence and also 688c2ecf20Sopenharmony_ci * pointing to the previous fence that was in the syncobj. The new struct 698c2ecf20Sopenharmony_ci * &dma_fence_chain fence replace the syncobj's fence and will be signaled by 708c2ecf20Sopenharmony_ci * completion of the DRM driver's work and also any work associated with the 718c2ecf20Sopenharmony_ci * fence previously in the syncobj. 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * When GPU work which waits on a syncobj is enqueued in a DRM driver, at the 748c2ecf20Sopenharmony_ci * time the work is enqueued, it waits on the syncobj's fence before 758c2ecf20Sopenharmony_ci * submitting the work to hardware. That fence is either : 768c2ecf20Sopenharmony_ci * 778c2ecf20Sopenharmony_ci * - The syncobj's current fence if the syncobj is considered as a binary 788c2ecf20Sopenharmony_ci * primitive. 798c2ecf20Sopenharmony_ci * - The struct &dma_fence associated with a given point if the syncobj is 808c2ecf20Sopenharmony_ci * considered as a timeline primitive. 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * If the syncobj's fence is NULL or not present in the syncobj's timeline, 838c2ecf20Sopenharmony_ci * the enqueue operation is expected to fail. 848c2ecf20Sopenharmony_ci * 858c2ecf20Sopenharmony_ci * With binary syncobj, all manipulation of the syncobjs's fence happens in 868c2ecf20Sopenharmony_ci * terms of the current fence at the time the ioctl is called by userspace 878c2ecf20Sopenharmony_ci * regardless of whether that operation is an immediate host-side operation 888c2ecf20Sopenharmony_ci * (signal or reset) or or an operation which is enqueued in some driver 898c2ecf20Sopenharmony_ci * queue. &DRM_IOCTL_SYNCOBJ_RESET and &DRM_IOCTL_SYNCOBJ_SIGNAL can be used 908c2ecf20Sopenharmony_ci * to manipulate a syncobj from the host by resetting its pointer to NULL or 918c2ecf20Sopenharmony_ci * setting its pointer to a fence which is already signaled. 928c2ecf20Sopenharmony_ci * 938c2ecf20Sopenharmony_ci * With a timeline syncobj, all manipulation of the synobj's fence happens in 948c2ecf20Sopenharmony_ci * terms of a u64 value referring to point in the timeline. See 958c2ecf20Sopenharmony_ci * dma_fence_chain_find_seqno() to see how a given point is found in the 968c2ecf20Sopenharmony_ci * timeline. 978c2ecf20Sopenharmony_ci * 988c2ecf20Sopenharmony_ci * Note that applications should be careful to always use timeline set of 998c2ecf20Sopenharmony_ci * ioctl() when dealing with syncobj considered as timeline. Using a binary 1008c2ecf20Sopenharmony_ci * set of ioctl() with a syncobj considered as timeline could result incorrect 1018c2ecf20Sopenharmony_ci * synchronization. The use of binary syncobj is supported through the 1028c2ecf20Sopenharmony_ci * timeline set of ioctl() by using a point value of 0, this will reproduce 1038c2ecf20Sopenharmony_ci * the behavior of the binary set of ioctl() (for example replace the 1048c2ecf20Sopenharmony_ci * syncobj's fence when signaling). 1058c2ecf20Sopenharmony_ci * 1068c2ecf20Sopenharmony_ci * 1078c2ecf20Sopenharmony_ci * Host-side wait on syncobjs 1088c2ecf20Sopenharmony_ci * -------------------------- 1098c2ecf20Sopenharmony_ci * 1108c2ecf20Sopenharmony_ci * &DRM_IOCTL_SYNCOBJ_WAIT takes an array of syncobj handles and does a 1118c2ecf20Sopenharmony_ci * host-side wait on all of the syncobj fences simultaneously. 1128c2ecf20Sopenharmony_ci * If &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL is set, the wait ioctl will wait on 1138c2ecf20Sopenharmony_ci * all of the syncobj fences to be signaled before it returns. 1148c2ecf20Sopenharmony_ci * Otherwise, it returns once at least one syncobj fence has been signaled 1158c2ecf20Sopenharmony_ci * and the index of a signaled fence is written back to the client. 1168c2ecf20Sopenharmony_ci * 1178c2ecf20Sopenharmony_ci * Unlike the enqueued GPU work dependencies which fail if they see a NULL 1188c2ecf20Sopenharmony_ci * fence in a syncobj, if &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT is set, 1198c2ecf20Sopenharmony_ci * the host-side wait will first wait for the syncobj to receive a non-NULL 1208c2ecf20Sopenharmony_ci * fence and then wait on that fence. 1218c2ecf20Sopenharmony_ci * If &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT is not set and any one of the 1228c2ecf20Sopenharmony_ci * syncobjs in the array has a NULL fence, -EINVAL will be returned. 1238c2ecf20Sopenharmony_ci * Assuming the syncobj starts off with a NULL fence, this allows a client 1248c2ecf20Sopenharmony_ci * to do a host wait in one thread (or process) which waits on GPU work 1258c2ecf20Sopenharmony_ci * submitted in another thread (or process) without having to manually 1268c2ecf20Sopenharmony_ci * synchronize between the two. 1278c2ecf20Sopenharmony_ci * This requirement is inherited from the Vulkan fence API. 1288c2ecf20Sopenharmony_ci * 1298c2ecf20Sopenharmony_ci * Similarly, &DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT takes an array of syncobj 1308c2ecf20Sopenharmony_ci * handles as well as an array of u64 points and does a host-side wait on all 1318c2ecf20Sopenharmony_ci * of syncobj fences at the given points simultaneously. 1328c2ecf20Sopenharmony_ci * 1338c2ecf20Sopenharmony_ci * &DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT also adds the ability to wait for a given 1348c2ecf20Sopenharmony_ci * fence to materialize on the timeline without waiting for the fence to be 1358c2ecf20Sopenharmony_ci * signaled by using the &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE flag. This 1368c2ecf20Sopenharmony_ci * requirement is inherited from the wait-before-signal behavior required by 1378c2ecf20Sopenharmony_ci * the Vulkan timeline semaphore API. 1388c2ecf20Sopenharmony_ci * 1398c2ecf20Sopenharmony_ci * 1408c2ecf20Sopenharmony_ci * Import/export of syncobjs 1418c2ecf20Sopenharmony_ci * ------------------------- 1428c2ecf20Sopenharmony_ci * 1438c2ecf20Sopenharmony_ci * &DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE and &DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD 1448c2ecf20Sopenharmony_ci * provide two mechanisms for import/export of syncobjs. 1458c2ecf20Sopenharmony_ci * 1468c2ecf20Sopenharmony_ci * The first lets the client import or export an entire syncobj to a file 1478c2ecf20Sopenharmony_ci * descriptor. 1488c2ecf20Sopenharmony_ci * These fd's are opaque and have no other use case, except passing the 1498c2ecf20Sopenharmony_ci * syncobj between processes. 1508c2ecf20Sopenharmony_ci * All exported file descriptors and any syncobj handles created as a 1518c2ecf20Sopenharmony_ci * result of importing those file descriptors own a reference to the 1528c2ecf20Sopenharmony_ci * same underlying struct &drm_syncobj and the syncobj can be used 1538c2ecf20Sopenharmony_ci * persistently across all the processes with which it is shared. 1548c2ecf20Sopenharmony_ci * The syncobj is freed only once the last reference is dropped. 1558c2ecf20Sopenharmony_ci * Unlike dma-buf, importing a syncobj creates a new handle (with its own 1568c2ecf20Sopenharmony_ci * reference) for every import instead of de-duplicating. 1578c2ecf20Sopenharmony_ci * The primary use-case of this persistent import/export is for shared 1588c2ecf20Sopenharmony_ci * Vulkan fences and semaphores. 1598c2ecf20Sopenharmony_ci * 1608c2ecf20Sopenharmony_ci * The second import/export mechanism, which is indicated by 1618c2ecf20Sopenharmony_ci * &DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE or 1628c2ecf20Sopenharmony_ci * &DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE lets the client 1638c2ecf20Sopenharmony_ci * import/export the syncobj's current fence from/to a &sync_file. 1648c2ecf20Sopenharmony_ci * When a syncobj is exported to a sync file, that sync file wraps the 1658c2ecf20Sopenharmony_ci * sycnobj's fence at the time of export and any later signal or reset 1668c2ecf20Sopenharmony_ci * operations on the syncobj will not affect the exported sync file. 1678c2ecf20Sopenharmony_ci * When a sync file is imported into a syncobj, the syncobj's fence is set 1688c2ecf20Sopenharmony_ci * to the fence wrapped by that sync file. 1698c2ecf20Sopenharmony_ci * Because sync files are immutable, resetting or signaling the syncobj 1708c2ecf20Sopenharmony_ci * will not affect any sync files whose fences have been imported into the 1718c2ecf20Sopenharmony_ci * syncobj. 1728c2ecf20Sopenharmony_ci * 1738c2ecf20Sopenharmony_ci * 1748c2ecf20Sopenharmony_ci * Import/export of timeline points in timeline syncobjs 1758c2ecf20Sopenharmony_ci * ----------------------------------------------------- 1768c2ecf20Sopenharmony_ci * 1778c2ecf20Sopenharmony_ci * &DRM_IOCTL_SYNCOBJ_TRANSFER provides a mechanism to transfer a struct 1788c2ecf20Sopenharmony_ci * &dma_fence_chain of a syncobj at a given u64 point to another u64 point 1798c2ecf20Sopenharmony_ci * into another syncobj. 1808c2ecf20Sopenharmony_ci * 1818c2ecf20Sopenharmony_ci * Note that if you want to transfer a struct &dma_fence_chain from a given 1828c2ecf20Sopenharmony_ci * point on a timeline syncobj from/into a binary syncobj, you can use the 1838c2ecf20Sopenharmony_ci * point 0 to mean take/replace the fence in the syncobj. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci#include <linux/anon_inodes.h> 1878c2ecf20Sopenharmony_ci#include <linux/file.h> 1888c2ecf20Sopenharmony_ci#include <linux/fs.h> 1898c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 1908c2ecf20Sopenharmony_ci#include <linux/sync_file.h> 1918c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci#include <drm/drm.h> 1948c2ecf20Sopenharmony_ci#include <drm/drm_drv.h> 1958c2ecf20Sopenharmony_ci#include <drm/drm_file.h> 1968c2ecf20Sopenharmony_ci#include <drm/drm_gem.h> 1978c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 1988c2ecf20Sopenharmony_ci#include <drm/drm_syncobj.h> 1998c2ecf20Sopenharmony_ci#include <drm/drm_utils.h> 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci#include "drm_internal.h" 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistruct syncobj_wait_entry { 2048c2ecf20Sopenharmony_ci struct list_head node; 2058c2ecf20Sopenharmony_ci struct task_struct *task; 2068c2ecf20Sopenharmony_ci struct dma_fence *fence; 2078c2ecf20Sopenharmony_ci struct dma_fence_cb fence_cb; 2088c2ecf20Sopenharmony_ci u64 point; 2098c2ecf20Sopenharmony_ci}; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj, 2128c2ecf20Sopenharmony_ci struct syncobj_wait_entry *wait); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci/** 2158c2ecf20Sopenharmony_ci * drm_syncobj_find - lookup and reference a sync object. 2168c2ecf20Sopenharmony_ci * @file_private: drm file private pointer 2178c2ecf20Sopenharmony_ci * @handle: sync object handle to lookup. 2188c2ecf20Sopenharmony_ci * 2198c2ecf20Sopenharmony_ci * Returns a reference to the syncobj pointed to by handle or NULL. The 2208c2ecf20Sopenharmony_ci * reference must be released by calling drm_syncobj_put(). 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_cistruct drm_syncobj *drm_syncobj_find(struct drm_file *file_private, 2238c2ecf20Sopenharmony_ci u32 handle) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct drm_syncobj *syncobj; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci spin_lock(&file_private->syncobj_table_lock); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* Check if we currently have a reference on the object */ 2308c2ecf20Sopenharmony_ci syncobj = idr_find(&file_private->syncobj_idr, handle); 2318c2ecf20Sopenharmony_ci if (syncobj) 2328c2ecf20Sopenharmony_ci drm_syncobj_get(syncobj); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci spin_unlock(&file_private->syncobj_table_lock); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return syncobj; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_syncobj_find); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic void drm_syncobj_fence_add_wait(struct drm_syncobj *syncobj, 2418c2ecf20Sopenharmony_ci struct syncobj_wait_entry *wait) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct dma_fence *fence; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (wait->fence) 2468c2ecf20Sopenharmony_ci return; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci spin_lock(&syncobj->lock); 2498c2ecf20Sopenharmony_ci /* We've already tried once to get a fence and failed. Now that we 2508c2ecf20Sopenharmony_ci * have the lock, try one more time just to be sure we don't add a 2518c2ecf20Sopenharmony_ci * callback when a fence has already been set. 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_ci fence = dma_fence_get(rcu_dereference_protected(syncobj->fence, 1)); 2548c2ecf20Sopenharmony_ci if (!fence || dma_fence_chain_find_seqno(&fence, wait->point)) { 2558c2ecf20Sopenharmony_ci dma_fence_put(fence); 2568c2ecf20Sopenharmony_ci list_add_tail(&wait->node, &syncobj->cb_list); 2578c2ecf20Sopenharmony_ci } else if (!fence) { 2588c2ecf20Sopenharmony_ci wait->fence = dma_fence_get_stub(); 2598c2ecf20Sopenharmony_ci } else { 2608c2ecf20Sopenharmony_ci wait->fence = fence; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci spin_unlock(&syncobj->lock); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic void drm_syncobj_remove_wait(struct drm_syncobj *syncobj, 2668c2ecf20Sopenharmony_ci struct syncobj_wait_entry *wait) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci if (!wait->node.next) 2698c2ecf20Sopenharmony_ci return; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci spin_lock(&syncobj->lock); 2728c2ecf20Sopenharmony_ci list_del_init(&wait->node); 2738c2ecf20Sopenharmony_ci spin_unlock(&syncobj->lock); 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci/** 2778c2ecf20Sopenharmony_ci * drm_syncobj_add_point - add new timeline point to the syncobj 2788c2ecf20Sopenharmony_ci * @syncobj: sync object to add timeline point do 2798c2ecf20Sopenharmony_ci * @chain: chain node to use to add the point 2808c2ecf20Sopenharmony_ci * @fence: fence to encapsulate in the chain node 2818c2ecf20Sopenharmony_ci * @point: sequence number to use for the point 2828c2ecf20Sopenharmony_ci * 2838c2ecf20Sopenharmony_ci * Add the chain node as new timeline point to the syncobj. 2848c2ecf20Sopenharmony_ci */ 2858c2ecf20Sopenharmony_civoid drm_syncobj_add_point(struct drm_syncobj *syncobj, 2868c2ecf20Sopenharmony_ci struct dma_fence_chain *chain, 2878c2ecf20Sopenharmony_ci struct dma_fence *fence, 2888c2ecf20Sopenharmony_ci uint64_t point) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct syncobj_wait_entry *cur, *tmp; 2918c2ecf20Sopenharmony_ci struct dma_fence *prev; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci dma_fence_get(fence); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci spin_lock(&syncobj->lock); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci prev = drm_syncobj_fence_get(syncobj); 2988c2ecf20Sopenharmony_ci /* You are adding an unorder point to timeline, which could cause payload returned from query_ioctl is 0! */ 2998c2ecf20Sopenharmony_ci if (prev && prev->seqno >= point) 3008c2ecf20Sopenharmony_ci DRM_DEBUG("You are adding an unorder point to timeline!\n"); 3018c2ecf20Sopenharmony_ci dma_fence_chain_init(chain, prev, fence, point); 3028c2ecf20Sopenharmony_ci rcu_assign_pointer(syncobj->fence, &chain->base); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci list_for_each_entry_safe(cur, tmp, &syncobj->cb_list, node) 3058c2ecf20Sopenharmony_ci syncobj_wait_syncobj_func(syncobj, cur); 3068c2ecf20Sopenharmony_ci spin_unlock(&syncobj->lock); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* Walk the chain once to trigger garbage collection */ 3098c2ecf20Sopenharmony_ci dma_fence_chain_for_each(fence, prev); 3108c2ecf20Sopenharmony_ci dma_fence_put(prev); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_syncobj_add_point); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci/** 3158c2ecf20Sopenharmony_ci * drm_syncobj_replace_fence - replace fence in a sync object. 3168c2ecf20Sopenharmony_ci * @syncobj: Sync object to replace fence in 3178c2ecf20Sopenharmony_ci * @fence: fence to install in sync file. 3188c2ecf20Sopenharmony_ci * 3198c2ecf20Sopenharmony_ci * This replaces the fence on a sync object. 3208c2ecf20Sopenharmony_ci */ 3218c2ecf20Sopenharmony_civoid drm_syncobj_replace_fence(struct drm_syncobj *syncobj, 3228c2ecf20Sopenharmony_ci struct dma_fence *fence) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci struct dma_fence *old_fence; 3258c2ecf20Sopenharmony_ci struct syncobj_wait_entry *cur, *tmp; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (fence) 3288c2ecf20Sopenharmony_ci dma_fence_get(fence); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci spin_lock(&syncobj->lock); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci old_fence = rcu_dereference_protected(syncobj->fence, 3338c2ecf20Sopenharmony_ci lockdep_is_held(&syncobj->lock)); 3348c2ecf20Sopenharmony_ci rcu_assign_pointer(syncobj->fence, fence); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (fence != old_fence) { 3378c2ecf20Sopenharmony_ci list_for_each_entry_safe(cur, tmp, &syncobj->cb_list, node) 3388c2ecf20Sopenharmony_ci syncobj_wait_syncobj_func(syncobj, cur); 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci spin_unlock(&syncobj->lock); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci dma_fence_put(old_fence); 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_syncobj_replace_fence); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci/** 3488c2ecf20Sopenharmony_ci * drm_syncobj_assign_null_handle - assign a stub fence to the sync object 3498c2ecf20Sopenharmony_ci * @syncobj: sync object to assign the fence on 3508c2ecf20Sopenharmony_ci * 3518c2ecf20Sopenharmony_ci * Assign a already signaled stub fence to the sync object. 3528c2ecf20Sopenharmony_ci */ 3538c2ecf20Sopenharmony_cistatic void drm_syncobj_assign_null_handle(struct drm_syncobj *syncobj) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct dma_fence *fence = dma_fence_get_stub(); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci drm_syncobj_replace_fence(syncobj, fence); 3588c2ecf20Sopenharmony_ci dma_fence_put(fence); 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci/* 5s default for wait submission */ 3628c2ecf20Sopenharmony_ci#define DRM_SYNCOBJ_WAIT_FOR_SUBMIT_TIMEOUT 5000000000ULL 3638c2ecf20Sopenharmony_ci/** 3648c2ecf20Sopenharmony_ci * drm_syncobj_find_fence - lookup and reference the fence in a sync object 3658c2ecf20Sopenharmony_ci * @file_private: drm file private pointer 3668c2ecf20Sopenharmony_ci * @handle: sync object handle to lookup. 3678c2ecf20Sopenharmony_ci * @point: timeline point 3688c2ecf20Sopenharmony_ci * @flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or not 3698c2ecf20Sopenharmony_ci * @fence: out parameter for the fence 3708c2ecf20Sopenharmony_ci * 3718c2ecf20Sopenharmony_ci * This is just a convenience function that combines drm_syncobj_find() and 3728c2ecf20Sopenharmony_ci * drm_syncobj_fence_get(). 3738c2ecf20Sopenharmony_ci * 3748c2ecf20Sopenharmony_ci * Returns 0 on success or a negative error value on failure. On success @fence 3758c2ecf20Sopenharmony_ci * contains a reference to the fence, which must be released by calling 3768c2ecf20Sopenharmony_ci * dma_fence_put(). 3778c2ecf20Sopenharmony_ci */ 3788c2ecf20Sopenharmony_ciint drm_syncobj_find_fence(struct drm_file *file_private, 3798c2ecf20Sopenharmony_ci u32 handle, u64 point, u64 flags, 3808c2ecf20Sopenharmony_ci struct dma_fence **fence) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle); 3838c2ecf20Sopenharmony_ci struct syncobj_wait_entry wait; 3848c2ecf20Sopenharmony_ci u64 timeout = nsecs_to_jiffies64(DRM_SYNCOBJ_WAIT_FOR_SUBMIT_TIMEOUT); 3858c2ecf20Sopenharmony_ci int ret; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (!syncobj) 3888c2ecf20Sopenharmony_ci return -ENOENT; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci *fence = drm_syncobj_fence_get(syncobj); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (*fence) { 3938c2ecf20Sopenharmony_ci ret = dma_fence_chain_find_seqno(fence, point); 3948c2ecf20Sopenharmony_ci if (!ret) { 3958c2ecf20Sopenharmony_ci /* If the requested seqno is already signaled 3968c2ecf20Sopenharmony_ci * drm_syncobj_find_fence may return a NULL 3978c2ecf20Sopenharmony_ci * fence. To make sure the recipient gets 3988c2ecf20Sopenharmony_ci * signalled, use a new fence instead. 3998c2ecf20Sopenharmony_ci */ 4008c2ecf20Sopenharmony_ci if (!*fence) 4018c2ecf20Sopenharmony_ci *fence = dma_fence_get_stub(); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci goto out; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci dma_fence_put(*fence); 4068c2ecf20Sopenharmony_ci } else { 4078c2ecf20Sopenharmony_ci ret = -EINVAL; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (!(flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT)) 4118c2ecf20Sopenharmony_ci goto out; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci memset(&wait, 0, sizeof(wait)); 4148c2ecf20Sopenharmony_ci wait.task = current; 4158c2ecf20Sopenharmony_ci wait.point = point; 4168c2ecf20Sopenharmony_ci drm_syncobj_fence_add_wait(syncobj, &wait); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci do { 4198c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 4208c2ecf20Sopenharmony_ci if (wait.fence) { 4218c2ecf20Sopenharmony_ci ret = 0; 4228c2ecf20Sopenharmony_ci break; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci if (timeout == 0) { 4258c2ecf20Sopenharmony_ci ret = -ETIME; 4268c2ecf20Sopenharmony_ci break; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (signal_pending(current)) { 4308c2ecf20Sopenharmony_ci ret = -ERESTARTSYS; 4318c2ecf20Sopenharmony_ci break; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci timeout = schedule_timeout(timeout); 4358c2ecf20Sopenharmony_ci } while (1); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci __set_current_state(TASK_RUNNING); 4388c2ecf20Sopenharmony_ci *fence = wait.fence; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (wait.node.next) 4418c2ecf20Sopenharmony_ci drm_syncobj_remove_wait(syncobj, &wait); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ciout: 4448c2ecf20Sopenharmony_ci drm_syncobj_put(syncobj); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci return ret; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_syncobj_find_fence); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci/** 4518c2ecf20Sopenharmony_ci * drm_syncobj_free - free a sync object. 4528c2ecf20Sopenharmony_ci * @kref: kref to free. 4538c2ecf20Sopenharmony_ci * 4548c2ecf20Sopenharmony_ci * Only to be called from kref_put in drm_syncobj_put. 4558c2ecf20Sopenharmony_ci */ 4568c2ecf20Sopenharmony_civoid drm_syncobj_free(struct kref *kref) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci struct drm_syncobj *syncobj = container_of(kref, 4598c2ecf20Sopenharmony_ci struct drm_syncobj, 4608c2ecf20Sopenharmony_ci refcount); 4618c2ecf20Sopenharmony_ci drm_syncobj_replace_fence(syncobj, NULL); 4628c2ecf20Sopenharmony_ci kfree(syncobj); 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_syncobj_free); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci/** 4678c2ecf20Sopenharmony_ci * drm_syncobj_create - create a new syncobj 4688c2ecf20Sopenharmony_ci * @out_syncobj: returned syncobj 4698c2ecf20Sopenharmony_ci * @flags: DRM_SYNCOBJ_* flags 4708c2ecf20Sopenharmony_ci * @fence: if non-NULL, the syncobj will represent this fence 4718c2ecf20Sopenharmony_ci * 4728c2ecf20Sopenharmony_ci * This is the first function to create a sync object. After creating, drivers 4738c2ecf20Sopenharmony_ci * probably want to make it available to userspace, either through 4748c2ecf20Sopenharmony_ci * drm_syncobj_get_handle() or drm_syncobj_get_fd(). 4758c2ecf20Sopenharmony_ci * 4768c2ecf20Sopenharmony_ci * Returns 0 on success or a negative error value on failure. 4778c2ecf20Sopenharmony_ci */ 4788c2ecf20Sopenharmony_ciint drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags, 4798c2ecf20Sopenharmony_ci struct dma_fence *fence) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct drm_syncobj *syncobj; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci syncobj = kzalloc(sizeof(struct drm_syncobj), GFP_KERNEL); 4848c2ecf20Sopenharmony_ci if (!syncobj) 4858c2ecf20Sopenharmony_ci return -ENOMEM; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci kref_init(&syncobj->refcount); 4888c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&syncobj->cb_list); 4898c2ecf20Sopenharmony_ci spin_lock_init(&syncobj->lock); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (flags & DRM_SYNCOBJ_CREATE_SIGNALED) 4928c2ecf20Sopenharmony_ci drm_syncobj_assign_null_handle(syncobj); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (fence) 4958c2ecf20Sopenharmony_ci drm_syncobj_replace_fence(syncobj, fence); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci *out_syncobj = syncobj; 4988c2ecf20Sopenharmony_ci return 0; 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_syncobj_create); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci/** 5038c2ecf20Sopenharmony_ci * drm_syncobj_get_handle - get a handle from a syncobj 5048c2ecf20Sopenharmony_ci * @file_private: drm file private pointer 5058c2ecf20Sopenharmony_ci * @syncobj: Sync object to export 5068c2ecf20Sopenharmony_ci * @handle: out parameter with the new handle 5078c2ecf20Sopenharmony_ci * 5088c2ecf20Sopenharmony_ci * Exports a sync object created with drm_syncobj_create() as a handle on 5098c2ecf20Sopenharmony_ci * @file_private to userspace. 5108c2ecf20Sopenharmony_ci * 5118c2ecf20Sopenharmony_ci * Returns 0 on success or a negative error value on failure. 5128c2ecf20Sopenharmony_ci */ 5138c2ecf20Sopenharmony_ciint drm_syncobj_get_handle(struct drm_file *file_private, 5148c2ecf20Sopenharmony_ci struct drm_syncobj *syncobj, u32 *handle) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci int ret; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* take a reference to put in the idr */ 5198c2ecf20Sopenharmony_ci drm_syncobj_get(syncobj); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci idr_preload(GFP_KERNEL); 5228c2ecf20Sopenharmony_ci spin_lock(&file_private->syncobj_table_lock); 5238c2ecf20Sopenharmony_ci ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT); 5248c2ecf20Sopenharmony_ci spin_unlock(&file_private->syncobj_table_lock); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci idr_preload_end(); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (ret < 0) { 5298c2ecf20Sopenharmony_ci drm_syncobj_put(syncobj); 5308c2ecf20Sopenharmony_ci return ret; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci *handle = ret; 5348c2ecf20Sopenharmony_ci return 0; 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_syncobj_get_handle); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic int drm_syncobj_create_as_handle(struct drm_file *file_private, 5398c2ecf20Sopenharmony_ci u32 *handle, uint32_t flags) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci int ret; 5428c2ecf20Sopenharmony_ci struct drm_syncobj *syncobj; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci ret = drm_syncobj_create(&syncobj, flags, NULL); 5458c2ecf20Sopenharmony_ci if (ret) 5468c2ecf20Sopenharmony_ci return ret; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci ret = drm_syncobj_get_handle(file_private, syncobj, handle); 5498c2ecf20Sopenharmony_ci drm_syncobj_put(syncobj); 5508c2ecf20Sopenharmony_ci return ret; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic int drm_syncobj_destroy(struct drm_file *file_private, 5548c2ecf20Sopenharmony_ci u32 handle) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct drm_syncobj *syncobj; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci spin_lock(&file_private->syncobj_table_lock); 5598c2ecf20Sopenharmony_ci syncobj = idr_remove(&file_private->syncobj_idr, handle); 5608c2ecf20Sopenharmony_ci spin_unlock(&file_private->syncobj_table_lock); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (!syncobj) 5638c2ecf20Sopenharmony_ci return -EINVAL; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci drm_syncobj_put(syncobj); 5668c2ecf20Sopenharmony_ci return 0; 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic int drm_syncobj_file_release(struct inode *inode, struct file *file) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci struct drm_syncobj *syncobj = file->private_data; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci drm_syncobj_put(syncobj); 5748c2ecf20Sopenharmony_ci return 0; 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic const struct file_operations drm_syncobj_file_fops = { 5788c2ecf20Sopenharmony_ci .release = drm_syncobj_file_release, 5798c2ecf20Sopenharmony_ci}; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci/** 5828c2ecf20Sopenharmony_ci * drm_syncobj_get_fd - get a file descriptor from a syncobj 5838c2ecf20Sopenharmony_ci * @syncobj: Sync object to export 5848c2ecf20Sopenharmony_ci * @p_fd: out parameter with the new file descriptor 5858c2ecf20Sopenharmony_ci * 5868c2ecf20Sopenharmony_ci * Exports a sync object created with drm_syncobj_create() as a file descriptor. 5878c2ecf20Sopenharmony_ci * 5888c2ecf20Sopenharmony_ci * Returns 0 on success or a negative error value on failure. 5898c2ecf20Sopenharmony_ci */ 5908c2ecf20Sopenharmony_ciint drm_syncobj_get_fd(struct drm_syncobj *syncobj, int *p_fd) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci struct file *file; 5938c2ecf20Sopenharmony_ci int fd; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci fd = get_unused_fd_flags(O_CLOEXEC); 5968c2ecf20Sopenharmony_ci if (fd < 0) 5978c2ecf20Sopenharmony_ci return fd; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci file = anon_inode_getfile("syncobj_file", 6008c2ecf20Sopenharmony_ci &drm_syncobj_file_fops, 6018c2ecf20Sopenharmony_ci syncobj, 0); 6028c2ecf20Sopenharmony_ci if (IS_ERR(file)) { 6038c2ecf20Sopenharmony_ci put_unused_fd(fd); 6048c2ecf20Sopenharmony_ci return PTR_ERR(file); 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci drm_syncobj_get(syncobj); 6088c2ecf20Sopenharmony_ci fd_install(fd, file); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci *p_fd = fd; 6118c2ecf20Sopenharmony_ci return 0; 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_syncobj_get_fd); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cistatic int drm_syncobj_handle_to_fd(struct drm_file *file_private, 6168c2ecf20Sopenharmony_ci u32 handle, int *p_fd) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle); 6198c2ecf20Sopenharmony_ci int ret; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (!syncobj) 6228c2ecf20Sopenharmony_ci return -EINVAL; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci ret = drm_syncobj_get_fd(syncobj, p_fd); 6258c2ecf20Sopenharmony_ci drm_syncobj_put(syncobj); 6268c2ecf20Sopenharmony_ci return ret; 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_cistatic int drm_syncobj_fd_to_handle(struct drm_file *file_private, 6308c2ecf20Sopenharmony_ci int fd, u32 *handle) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci struct drm_syncobj *syncobj; 6338c2ecf20Sopenharmony_ci struct fd f = fdget(fd); 6348c2ecf20Sopenharmony_ci int ret; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci if (!f.file) 6378c2ecf20Sopenharmony_ci return -EINVAL; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci if (f.file->f_op != &drm_syncobj_file_fops) { 6408c2ecf20Sopenharmony_ci fdput(f); 6418c2ecf20Sopenharmony_ci return -EINVAL; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci /* take a reference to put in the idr */ 6458c2ecf20Sopenharmony_ci syncobj = f.file->private_data; 6468c2ecf20Sopenharmony_ci drm_syncobj_get(syncobj); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci idr_preload(GFP_KERNEL); 6498c2ecf20Sopenharmony_ci spin_lock(&file_private->syncobj_table_lock); 6508c2ecf20Sopenharmony_ci ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT); 6518c2ecf20Sopenharmony_ci spin_unlock(&file_private->syncobj_table_lock); 6528c2ecf20Sopenharmony_ci idr_preload_end(); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci if (ret > 0) { 6558c2ecf20Sopenharmony_ci *handle = ret; 6568c2ecf20Sopenharmony_ci ret = 0; 6578c2ecf20Sopenharmony_ci } else 6588c2ecf20Sopenharmony_ci drm_syncobj_put(syncobj); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci fdput(f); 6618c2ecf20Sopenharmony_ci return ret; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic int drm_syncobj_import_sync_file_fence(struct drm_file *file_private, 6658c2ecf20Sopenharmony_ci int fd, int handle) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci struct dma_fence *fence = sync_file_get_fence(fd); 6688c2ecf20Sopenharmony_ci struct drm_syncobj *syncobj; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if (!fence) 6718c2ecf20Sopenharmony_ci return -EINVAL; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci syncobj = drm_syncobj_find(file_private, handle); 6748c2ecf20Sopenharmony_ci if (!syncobj) { 6758c2ecf20Sopenharmony_ci dma_fence_put(fence); 6768c2ecf20Sopenharmony_ci return -ENOENT; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci drm_syncobj_replace_fence(syncobj, fence); 6808c2ecf20Sopenharmony_ci dma_fence_put(fence); 6818c2ecf20Sopenharmony_ci drm_syncobj_put(syncobj); 6828c2ecf20Sopenharmony_ci return 0; 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_cistatic int drm_syncobj_export_sync_file(struct drm_file *file_private, 6868c2ecf20Sopenharmony_ci int handle, int *p_fd) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci int ret; 6898c2ecf20Sopenharmony_ci struct dma_fence *fence; 6908c2ecf20Sopenharmony_ci struct sync_file *sync_file; 6918c2ecf20Sopenharmony_ci int fd = get_unused_fd_flags(O_CLOEXEC); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci if (fd < 0) 6948c2ecf20Sopenharmony_ci return fd; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci ret = drm_syncobj_find_fence(file_private, handle, 0, 0, &fence); 6978c2ecf20Sopenharmony_ci if (ret) 6988c2ecf20Sopenharmony_ci goto err_put_fd; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci sync_file = sync_file_create(fence); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci dma_fence_put(fence); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (!sync_file) { 7058c2ecf20Sopenharmony_ci ret = -EINVAL; 7068c2ecf20Sopenharmony_ci goto err_put_fd; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci fd_install(fd, sync_file->file); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci *p_fd = fd; 7128c2ecf20Sopenharmony_ci return 0; 7138c2ecf20Sopenharmony_cierr_put_fd: 7148c2ecf20Sopenharmony_ci put_unused_fd(fd); 7158c2ecf20Sopenharmony_ci return ret; 7168c2ecf20Sopenharmony_ci} 7178c2ecf20Sopenharmony_ci/** 7188c2ecf20Sopenharmony_ci * drm_syncobj_open - initalizes syncobj file-private structures at devnode open time 7198c2ecf20Sopenharmony_ci * @file_private: drm file-private structure to set up 7208c2ecf20Sopenharmony_ci * 7218c2ecf20Sopenharmony_ci * Called at device open time, sets up the structure for handling refcounting 7228c2ecf20Sopenharmony_ci * of sync objects. 7238c2ecf20Sopenharmony_ci */ 7248c2ecf20Sopenharmony_civoid 7258c2ecf20Sopenharmony_cidrm_syncobj_open(struct drm_file *file_private) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci idr_init_base(&file_private->syncobj_idr, 1); 7288c2ecf20Sopenharmony_ci spin_lock_init(&file_private->syncobj_table_lock); 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cistatic int 7328c2ecf20Sopenharmony_cidrm_syncobj_release_handle(int id, void *ptr, void *data) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci struct drm_syncobj *syncobj = ptr; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci drm_syncobj_put(syncobj); 7378c2ecf20Sopenharmony_ci return 0; 7388c2ecf20Sopenharmony_ci} 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci/** 7418c2ecf20Sopenharmony_ci * drm_syncobj_release - release file-private sync object resources 7428c2ecf20Sopenharmony_ci * @file_private: drm file-private structure to clean up 7438c2ecf20Sopenharmony_ci * 7448c2ecf20Sopenharmony_ci * Called at close time when the filp is going away. 7458c2ecf20Sopenharmony_ci * 7468c2ecf20Sopenharmony_ci * Releases any remaining references on objects by this filp. 7478c2ecf20Sopenharmony_ci */ 7488c2ecf20Sopenharmony_civoid 7498c2ecf20Sopenharmony_cidrm_syncobj_release(struct drm_file *file_private) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci idr_for_each(&file_private->syncobj_idr, 7528c2ecf20Sopenharmony_ci &drm_syncobj_release_handle, file_private); 7538c2ecf20Sopenharmony_ci idr_destroy(&file_private->syncobj_idr); 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ciint 7578c2ecf20Sopenharmony_cidrm_syncobj_create_ioctl(struct drm_device *dev, void *data, 7588c2ecf20Sopenharmony_ci struct drm_file *file_private) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci struct drm_syncobj_create *args = data; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ)) 7638c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci /* no valid flags yet */ 7668c2ecf20Sopenharmony_ci if (args->flags & ~DRM_SYNCOBJ_CREATE_SIGNALED) 7678c2ecf20Sopenharmony_ci return -EINVAL; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci return drm_syncobj_create_as_handle(file_private, 7708c2ecf20Sopenharmony_ci &args->handle, args->flags); 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ciint 7748c2ecf20Sopenharmony_cidrm_syncobj_destroy_ioctl(struct drm_device *dev, void *data, 7758c2ecf20Sopenharmony_ci struct drm_file *file_private) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci struct drm_syncobj_destroy *args = data; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ)) 7808c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci /* make sure padding is empty */ 7838c2ecf20Sopenharmony_ci if (args->pad) 7848c2ecf20Sopenharmony_ci return -EINVAL; 7858c2ecf20Sopenharmony_ci return drm_syncobj_destroy(file_private, args->handle); 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ciint 7898c2ecf20Sopenharmony_cidrm_syncobj_handle_to_fd_ioctl(struct drm_device *dev, void *data, 7908c2ecf20Sopenharmony_ci struct drm_file *file_private) 7918c2ecf20Sopenharmony_ci{ 7928c2ecf20Sopenharmony_ci struct drm_syncobj_handle *args = data; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ)) 7958c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci if (args->pad) 7988c2ecf20Sopenharmony_ci return -EINVAL; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci if (args->flags != 0 && 8018c2ecf20Sopenharmony_ci args->flags != DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE) 8028c2ecf20Sopenharmony_ci return -EINVAL; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci if (args->flags & DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE) 8058c2ecf20Sopenharmony_ci return drm_syncobj_export_sync_file(file_private, args->handle, 8068c2ecf20Sopenharmony_ci &args->fd); 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci return drm_syncobj_handle_to_fd(file_private, args->handle, 8098c2ecf20Sopenharmony_ci &args->fd); 8108c2ecf20Sopenharmony_ci} 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ciint 8138c2ecf20Sopenharmony_cidrm_syncobj_fd_to_handle_ioctl(struct drm_device *dev, void *data, 8148c2ecf20Sopenharmony_ci struct drm_file *file_private) 8158c2ecf20Sopenharmony_ci{ 8168c2ecf20Sopenharmony_ci struct drm_syncobj_handle *args = data; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ)) 8198c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci if (args->pad) 8228c2ecf20Sopenharmony_ci return -EINVAL; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci if (args->flags != 0 && 8258c2ecf20Sopenharmony_ci args->flags != DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE) 8268c2ecf20Sopenharmony_ci return -EINVAL; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci if (args->flags & DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE) 8298c2ecf20Sopenharmony_ci return drm_syncobj_import_sync_file_fence(file_private, 8308c2ecf20Sopenharmony_ci args->fd, 8318c2ecf20Sopenharmony_ci args->handle); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci return drm_syncobj_fd_to_handle(file_private, args->fd, 8348c2ecf20Sopenharmony_ci &args->handle); 8358c2ecf20Sopenharmony_ci} 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_cistatic int drm_syncobj_transfer_to_timeline(struct drm_file *file_private, 8388c2ecf20Sopenharmony_ci struct drm_syncobj_transfer *args) 8398c2ecf20Sopenharmony_ci{ 8408c2ecf20Sopenharmony_ci struct drm_syncobj *timeline_syncobj = NULL; 8418c2ecf20Sopenharmony_ci struct dma_fence *fence; 8428c2ecf20Sopenharmony_ci struct dma_fence_chain *chain; 8438c2ecf20Sopenharmony_ci int ret; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci timeline_syncobj = drm_syncobj_find(file_private, args->dst_handle); 8468c2ecf20Sopenharmony_ci if (!timeline_syncobj) { 8478c2ecf20Sopenharmony_ci return -ENOENT; 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci ret = drm_syncobj_find_fence(file_private, args->src_handle, 8508c2ecf20Sopenharmony_ci args->src_point, args->flags, 8518c2ecf20Sopenharmony_ci &fence); 8528c2ecf20Sopenharmony_ci if (ret) 8538c2ecf20Sopenharmony_ci goto err; 8548c2ecf20Sopenharmony_ci chain = kzalloc(sizeof(struct dma_fence_chain), GFP_KERNEL); 8558c2ecf20Sopenharmony_ci if (!chain) { 8568c2ecf20Sopenharmony_ci ret = -ENOMEM; 8578c2ecf20Sopenharmony_ci goto err1; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci drm_syncobj_add_point(timeline_syncobj, chain, fence, args->dst_point); 8608c2ecf20Sopenharmony_cierr1: 8618c2ecf20Sopenharmony_ci dma_fence_put(fence); 8628c2ecf20Sopenharmony_cierr: 8638c2ecf20Sopenharmony_ci drm_syncobj_put(timeline_syncobj); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci return ret; 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_cistatic int 8698c2ecf20Sopenharmony_cidrm_syncobj_transfer_to_binary(struct drm_file *file_private, 8708c2ecf20Sopenharmony_ci struct drm_syncobj_transfer *args) 8718c2ecf20Sopenharmony_ci{ 8728c2ecf20Sopenharmony_ci struct drm_syncobj *binary_syncobj = NULL; 8738c2ecf20Sopenharmony_ci struct dma_fence *fence; 8748c2ecf20Sopenharmony_ci int ret; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci binary_syncobj = drm_syncobj_find(file_private, args->dst_handle); 8778c2ecf20Sopenharmony_ci if (!binary_syncobj) 8788c2ecf20Sopenharmony_ci return -ENOENT; 8798c2ecf20Sopenharmony_ci ret = drm_syncobj_find_fence(file_private, args->src_handle, 8808c2ecf20Sopenharmony_ci args->src_point, args->flags, &fence); 8818c2ecf20Sopenharmony_ci if (ret) 8828c2ecf20Sopenharmony_ci goto err; 8838c2ecf20Sopenharmony_ci drm_syncobj_replace_fence(binary_syncobj, fence); 8848c2ecf20Sopenharmony_ci dma_fence_put(fence); 8858c2ecf20Sopenharmony_cierr: 8868c2ecf20Sopenharmony_ci drm_syncobj_put(binary_syncobj); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci return ret; 8898c2ecf20Sopenharmony_ci} 8908c2ecf20Sopenharmony_ciint 8918c2ecf20Sopenharmony_cidrm_syncobj_transfer_ioctl(struct drm_device *dev, void *data, 8928c2ecf20Sopenharmony_ci struct drm_file *file_private) 8938c2ecf20Sopenharmony_ci{ 8948c2ecf20Sopenharmony_ci struct drm_syncobj_transfer *args = data; 8958c2ecf20Sopenharmony_ci int ret; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE)) 8988c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci if (args->pad) 9018c2ecf20Sopenharmony_ci return -EINVAL; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci if (args->dst_point) 9048c2ecf20Sopenharmony_ci ret = drm_syncobj_transfer_to_timeline(file_private, args); 9058c2ecf20Sopenharmony_ci else 9068c2ecf20Sopenharmony_ci ret = drm_syncobj_transfer_to_binary(file_private, args); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci return ret; 9098c2ecf20Sopenharmony_ci} 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_cistatic void syncobj_wait_fence_func(struct dma_fence *fence, 9128c2ecf20Sopenharmony_ci struct dma_fence_cb *cb) 9138c2ecf20Sopenharmony_ci{ 9148c2ecf20Sopenharmony_ci struct syncobj_wait_entry *wait = 9158c2ecf20Sopenharmony_ci container_of(cb, struct syncobj_wait_entry, fence_cb); 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci wake_up_process(wait->task); 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_cistatic void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj, 9218c2ecf20Sopenharmony_ci struct syncobj_wait_entry *wait) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci struct dma_fence *fence; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci /* This happens inside the syncobj lock */ 9268c2ecf20Sopenharmony_ci fence = rcu_dereference_protected(syncobj->fence, 9278c2ecf20Sopenharmony_ci lockdep_is_held(&syncobj->lock)); 9288c2ecf20Sopenharmony_ci dma_fence_get(fence); 9298c2ecf20Sopenharmony_ci if (!fence || dma_fence_chain_find_seqno(&fence, wait->point)) { 9308c2ecf20Sopenharmony_ci dma_fence_put(fence); 9318c2ecf20Sopenharmony_ci return; 9328c2ecf20Sopenharmony_ci } else if (!fence) { 9338c2ecf20Sopenharmony_ci wait->fence = dma_fence_get_stub(); 9348c2ecf20Sopenharmony_ci } else { 9358c2ecf20Sopenharmony_ci wait->fence = fence; 9368c2ecf20Sopenharmony_ci } 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci wake_up_process(wait->task); 9398c2ecf20Sopenharmony_ci list_del_init(&wait->node); 9408c2ecf20Sopenharmony_ci} 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_cistatic signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs, 9438c2ecf20Sopenharmony_ci void __user *user_points, 9448c2ecf20Sopenharmony_ci uint32_t count, 9458c2ecf20Sopenharmony_ci uint32_t flags, 9468c2ecf20Sopenharmony_ci signed long timeout, 9478c2ecf20Sopenharmony_ci uint32_t *idx) 9488c2ecf20Sopenharmony_ci{ 9498c2ecf20Sopenharmony_ci struct syncobj_wait_entry *entries; 9508c2ecf20Sopenharmony_ci struct dma_fence *fence; 9518c2ecf20Sopenharmony_ci uint64_t *points; 9528c2ecf20Sopenharmony_ci uint32_t signaled_count, i; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci points = kmalloc_array(count, sizeof(*points), GFP_KERNEL); 9558c2ecf20Sopenharmony_ci if (points == NULL) 9568c2ecf20Sopenharmony_ci return -ENOMEM; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci if (!user_points) { 9598c2ecf20Sopenharmony_ci memset(points, 0, count * sizeof(uint64_t)); 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci } else if (copy_from_user(points, user_points, 9628c2ecf20Sopenharmony_ci sizeof(uint64_t) * count)) { 9638c2ecf20Sopenharmony_ci timeout = -EFAULT; 9648c2ecf20Sopenharmony_ci goto err_free_points; 9658c2ecf20Sopenharmony_ci } 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci entries = kcalloc(count, sizeof(*entries), GFP_KERNEL); 9688c2ecf20Sopenharmony_ci if (!entries) { 9698c2ecf20Sopenharmony_ci timeout = -ENOMEM; 9708c2ecf20Sopenharmony_ci goto err_free_points; 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci /* Walk the list of sync objects and initialize entries. We do 9738c2ecf20Sopenharmony_ci * this up-front so that we can properly return -EINVAL if there is 9748c2ecf20Sopenharmony_ci * a syncobj with a missing fence and then never have the chance of 9758c2ecf20Sopenharmony_ci * returning -EINVAL again. 9768c2ecf20Sopenharmony_ci */ 9778c2ecf20Sopenharmony_ci signaled_count = 0; 9788c2ecf20Sopenharmony_ci for (i = 0; i < count; ++i) { 9798c2ecf20Sopenharmony_ci struct dma_fence *fence; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci entries[i].task = current; 9828c2ecf20Sopenharmony_ci entries[i].point = points[i]; 9838c2ecf20Sopenharmony_ci fence = drm_syncobj_fence_get(syncobjs[i]); 9848c2ecf20Sopenharmony_ci if (!fence || dma_fence_chain_find_seqno(&fence, points[i])) { 9858c2ecf20Sopenharmony_ci dma_fence_put(fence); 9868c2ecf20Sopenharmony_ci if (flags & (DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT | 9878c2ecf20Sopenharmony_ci DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE)) { 9888c2ecf20Sopenharmony_ci continue; 9898c2ecf20Sopenharmony_ci } else { 9908c2ecf20Sopenharmony_ci timeout = -EINVAL; 9918c2ecf20Sopenharmony_ci goto cleanup_entries; 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci if (fence) 9968c2ecf20Sopenharmony_ci entries[i].fence = fence; 9978c2ecf20Sopenharmony_ci else 9988c2ecf20Sopenharmony_ci entries[i].fence = dma_fence_get_stub(); 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci if ((flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE) || 10018c2ecf20Sopenharmony_ci dma_fence_is_signaled(entries[i].fence)) { 10028c2ecf20Sopenharmony_ci if (signaled_count == 0 && idx) 10038c2ecf20Sopenharmony_ci *idx = i; 10048c2ecf20Sopenharmony_ci signaled_count++; 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci if (signaled_count == count || 10098c2ecf20Sopenharmony_ci (signaled_count > 0 && 10108c2ecf20Sopenharmony_ci !(flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL))) 10118c2ecf20Sopenharmony_ci goto cleanup_entries; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci /* There's a very annoying laxness in the dma_fence API here, in 10148c2ecf20Sopenharmony_ci * that backends are not required to automatically report when a 10158c2ecf20Sopenharmony_ci * fence is signaled prior to fence->ops->enable_signaling() being 10168c2ecf20Sopenharmony_ci * called. So here if we fail to match signaled_count, we need to 10178c2ecf20Sopenharmony_ci * fallthough and try a 0 timeout wait! 10188c2ecf20Sopenharmony_ci */ 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) { 10218c2ecf20Sopenharmony_ci for (i = 0; i < count; ++i) 10228c2ecf20Sopenharmony_ci drm_syncobj_fence_add_wait(syncobjs[i], &entries[i]); 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci do { 10268c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci signaled_count = 0; 10298c2ecf20Sopenharmony_ci for (i = 0; i < count; ++i) { 10308c2ecf20Sopenharmony_ci fence = entries[i].fence; 10318c2ecf20Sopenharmony_ci if (!fence) 10328c2ecf20Sopenharmony_ci continue; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci if ((flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE) || 10358c2ecf20Sopenharmony_ci dma_fence_is_signaled(fence) || 10368c2ecf20Sopenharmony_ci (!entries[i].fence_cb.func && 10378c2ecf20Sopenharmony_ci dma_fence_add_callback(fence, 10388c2ecf20Sopenharmony_ci &entries[i].fence_cb, 10398c2ecf20Sopenharmony_ci syncobj_wait_fence_func))) { 10408c2ecf20Sopenharmony_ci /* The fence has been signaled */ 10418c2ecf20Sopenharmony_ci if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL) { 10428c2ecf20Sopenharmony_ci signaled_count++; 10438c2ecf20Sopenharmony_ci } else { 10448c2ecf20Sopenharmony_ci if (idx) 10458c2ecf20Sopenharmony_ci *idx = i; 10468c2ecf20Sopenharmony_ci goto done_waiting; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci if (signaled_count == count) 10528c2ecf20Sopenharmony_ci goto done_waiting; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci if (timeout == 0) { 10558c2ecf20Sopenharmony_ci timeout = -ETIME; 10568c2ecf20Sopenharmony_ci goto done_waiting; 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci if (signal_pending(current)) { 10608c2ecf20Sopenharmony_ci timeout = -ERESTARTSYS; 10618c2ecf20Sopenharmony_ci goto done_waiting; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci timeout = schedule_timeout(timeout); 10658c2ecf20Sopenharmony_ci } while (1); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_cidone_waiting: 10688c2ecf20Sopenharmony_ci __set_current_state(TASK_RUNNING); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_cicleanup_entries: 10718c2ecf20Sopenharmony_ci for (i = 0; i < count; ++i) { 10728c2ecf20Sopenharmony_ci drm_syncobj_remove_wait(syncobjs[i], &entries[i]); 10738c2ecf20Sopenharmony_ci if (entries[i].fence_cb.func) 10748c2ecf20Sopenharmony_ci dma_fence_remove_callback(entries[i].fence, 10758c2ecf20Sopenharmony_ci &entries[i].fence_cb); 10768c2ecf20Sopenharmony_ci dma_fence_put(entries[i].fence); 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci kfree(entries); 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_cierr_free_points: 10818c2ecf20Sopenharmony_ci kfree(points); 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci return timeout; 10848c2ecf20Sopenharmony_ci} 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci/** 10878c2ecf20Sopenharmony_ci * drm_timeout_abs_to_jiffies - calculate jiffies timeout from absolute value 10888c2ecf20Sopenharmony_ci * 10898c2ecf20Sopenharmony_ci * @timeout_nsec: timeout nsec component in ns, 0 for poll 10908c2ecf20Sopenharmony_ci * 10918c2ecf20Sopenharmony_ci * Calculate the timeout in jiffies from an absolute time in sec/nsec. 10928c2ecf20Sopenharmony_ci */ 10938c2ecf20Sopenharmony_cisigned long drm_timeout_abs_to_jiffies(int64_t timeout_nsec) 10948c2ecf20Sopenharmony_ci{ 10958c2ecf20Sopenharmony_ci ktime_t abs_timeout, now; 10968c2ecf20Sopenharmony_ci u64 timeout_ns, timeout_jiffies64; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci /* make 0 timeout means poll - absolute 0 doesn't seem valid */ 10998c2ecf20Sopenharmony_ci if (timeout_nsec == 0) 11008c2ecf20Sopenharmony_ci return 0; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci abs_timeout = ns_to_ktime(timeout_nsec); 11038c2ecf20Sopenharmony_ci now = ktime_get(); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci if (!ktime_after(abs_timeout, now)) 11068c2ecf20Sopenharmony_ci return 0; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci timeout_ns = ktime_to_ns(ktime_sub(abs_timeout, now)); 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci timeout_jiffies64 = nsecs_to_jiffies64(timeout_ns); 11118c2ecf20Sopenharmony_ci /* clamp timeout to avoid infinite timeout */ 11128c2ecf20Sopenharmony_ci if (timeout_jiffies64 >= MAX_SCHEDULE_TIMEOUT - 1) 11138c2ecf20Sopenharmony_ci return MAX_SCHEDULE_TIMEOUT - 1; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci return timeout_jiffies64 + 1; 11168c2ecf20Sopenharmony_ci} 11178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_timeout_abs_to_jiffies); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_cistatic int drm_syncobj_array_wait(struct drm_device *dev, 11208c2ecf20Sopenharmony_ci struct drm_file *file_private, 11218c2ecf20Sopenharmony_ci struct drm_syncobj_wait *wait, 11228c2ecf20Sopenharmony_ci struct drm_syncobj_timeline_wait *timeline_wait, 11238c2ecf20Sopenharmony_ci struct drm_syncobj **syncobjs, bool timeline) 11248c2ecf20Sopenharmony_ci{ 11258c2ecf20Sopenharmony_ci signed long timeout = 0; 11268c2ecf20Sopenharmony_ci uint32_t first = ~0; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci if (!timeline) { 11298c2ecf20Sopenharmony_ci timeout = drm_timeout_abs_to_jiffies(wait->timeout_nsec); 11308c2ecf20Sopenharmony_ci timeout = drm_syncobj_array_wait_timeout(syncobjs, 11318c2ecf20Sopenharmony_ci NULL, 11328c2ecf20Sopenharmony_ci wait->count_handles, 11338c2ecf20Sopenharmony_ci wait->flags, 11348c2ecf20Sopenharmony_ci timeout, &first); 11358c2ecf20Sopenharmony_ci if (timeout < 0) 11368c2ecf20Sopenharmony_ci return timeout; 11378c2ecf20Sopenharmony_ci wait->first_signaled = first; 11388c2ecf20Sopenharmony_ci } else { 11398c2ecf20Sopenharmony_ci timeout = drm_timeout_abs_to_jiffies(timeline_wait->timeout_nsec); 11408c2ecf20Sopenharmony_ci timeout = drm_syncobj_array_wait_timeout(syncobjs, 11418c2ecf20Sopenharmony_ci u64_to_user_ptr(timeline_wait->points), 11428c2ecf20Sopenharmony_ci timeline_wait->count_handles, 11438c2ecf20Sopenharmony_ci timeline_wait->flags, 11448c2ecf20Sopenharmony_ci timeout, &first); 11458c2ecf20Sopenharmony_ci if (timeout < 0) 11468c2ecf20Sopenharmony_ci return timeout; 11478c2ecf20Sopenharmony_ci timeline_wait->first_signaled = first; 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci return 0; 11508c2ecf20Sopenharmony_ci} 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_cistatic int drm_syncobj_array_find(struct drm_file *file_private, 11538c2ecf20Sopenharmony_ci void __user *user_handles, 11548c2ecf20Sopenharmony_ci uint32_t count_handles, 11558c2ecf20Sopenharmony_ci struct drm_syncobj ***syncobjs_out) 11568c2ecf20Sopenharmony_ci{ 11578c2ecf20Sopenharmony_ci uint32_t i, *handles; 11588c2ecf20Sopenharmony_ci struct drm_syncobj **syncobjs; 11598c2ecf20Sopenharmony_ci int ret; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci handles = kmalloc_array(count_handles, sizeof(*handles), GFP_KERNEL); 11628c2ecf20Sopenharmony_ci if (handles == NULL) 11638c2ecf20Sopenharmony_ci return -ENOMEM; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci if (copy_from_user(handles, user_handles, 11668c2ecf20Sopenharmony_ci sizeof(uint32_t) * count_handles)) { 11678c2ecf20Sopenharmony_ci ret = -EFAULT; 11688c2ecf20Sopenharmony_ci goto err_free_handles; 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci syncobjs = kmalloc_array(count_handles, sizeof(*syncobjs), GFP_KERNEL); 11728c2ecf20Sopenharmony_ci if (syncobjs == NULL) { 11738c2ecf20Sopenharmony_ci ret = -ENOMEM; 11748c2ecf20Sopenharmony_ci goto err_free_handles; 11758c2ecf20Sopenharmony_ci } 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci for (i = 0; i < count_handles; i++) { 11788c2ecf20Sopenharmony_ci syncobjs[i] = drm_syncobj_find(file_private, handles[i]); 11798c2ecf20Sopenharmony_ci if (!syncobjs[i]) { 11808c2ecf20Sopenharmony_ci ret = -ENOENT; 11818c2ecf20Sopenharmony_ci goto err_put_syncobjs; 11828c2ecf20Sopenharmony_ci } 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci kfree(handles); 11868c2ecf20Sopenharmony_ci *syncobjs_out = syncobjs; 11878c2ecf20Sopenharmony_ci return 0; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_cierr_put_syncobjs: 11908c2ecf20Sopenharmony_ci while (i-- > 0) 11918c2ecf20Sopenharmony_ci drm_syncobj_put(syncobjs[i]); 11928c2ecf20Sopenharmony_ci kfree(syncobjs); 11938c2ecf20Sopenharmony_cierr_free_handles: 11948c2ecf20Sopenharmony_ci kfree(handles); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci return ret; 11978c2ecf20Sopenharmony_ci} 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_cistatic void drm_syncobj_array_free(struct drm_syncobj **syncobjs, 12008c2ecf20Sopenharmony_ci uint32_t count) 12018c2ecf20Sopenharmony_ci{ 12028c2ecf20Sopenharmony_ci uint32_t i; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) 12058c2ecf20Sopenharmony_ci drm_syncobj_put(syncobjs[i]); 12068c2ecf20Sopenharmony_ci kfree(syncobjs); 12078c2ecf20Sopenharmony_ci} 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ciint 12108c2ecf20Sopenharmony_cidrm_syncobj_wait_ioctl(struct drm_device *dev, void *data, 12118c2ecf20Sopenharmony_ci struct drm_file *file_private) 12128c2ecf20Sopenharmony_ci{ 12138c2ecf20Sopenharmony_ci struct drm_syncobj_wait *args = data; 12148c2ecf20Sopenharmony_ci struct drm_syncobj **syncobjs; 12158c2ecf20Sopenharmony_ci int ret = 0; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ)) 12188c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci if (args->flags & ~(DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL | 12218c2ecf20Sopenharmony_ci DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT)) 12228c2ecf20Sopenharmony_ci return -EINVAL; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci if (args->count_handles == 0) 12258c2ecf20Sopenharmony_ci return -EINVAL; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci ret = drm_syncobj_array_find(file_private, 12288c2ecf20Sopenharmony_ci u64_to_user_ptr(args->handles), 12298c2ecf20Sopenharmony_ci args->count_handles, 12308c2ecf20Sopenharmony_ci &syncobjs); 12318c2ecf20Sopenharmony_ci if (ret < 0) 12328c2ecf20Sopenharmony_ci return ret; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci ret = drm_syncobj_array_wait(dev, file_private, 12358c2ecf20Sopenharmony_ci args, NULL, syncobjs, false); 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci drm_syncobj_array_free(syncobjs, args->count_handles); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci return ret; 12408c2ecf20Sopenharmony_ci} 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ciint 12438c2ecf20Sopenharmony_cidrm_syncobj_timeline_wait_ioctl(struct drm_device *dev, void *data, 12448c2ecf20Sopenharmony_ci struct drm_file *file_private) 12458c2ecf20Sopenharmony_ci{ 12468c2ecf20Sopenharmony_ci struct drm_syncobj_timeline_wait *args = data; 12478c2ecf20Sopenharmony_ci struct drm_syncobj **syncobjs; 12488c2ecf20Sopenharmony_ci int ret = 0; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE)) 12518c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci if (args->flags & ~(DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL | 12548c2ecf20Sopenharmony_ci DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT | 12558c2ecf20Sopenharmony_ci DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE)) 12568c2ecf20Sopenharmony_ci return -EINVAL; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci if (args->count_handles == 0) 12598c2ecf20Sopenharmony_ci return -EINVAL; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci ret = drm_syncobj_array_find(file_private, 12628c2ecf20Sopenharmony_ci u64_to_user_ptr(args->handles), 12638c2ecf20Sopenharmony_ci args->count_handles, 12648c2ecf20Sopenharmony_ci &syncobjs); 12658c2ecf20Sopenharmony_ci if (ret < 0) 12668c2ecf20Sopenharmony_ci return ret; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci ret = drm_syncobj_array_wait(dev, file_private, 12698c2ecf20Sopenharmony_ci NULL, args, syncobjs, true); 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci drm_syncobj_array_free(syncobjs, args->count_handles); 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci return ret; 12748c2ecf20Sopenharmony_ci} 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ciint 12788c2ecf20Sopenharmony_cidrm_syncobj_reset_ioctl(struct drm_device *dev, void *data, 12798c2ecf20Sopenharmony_ci struct drm_file *file_private) 12808c2ecf20Sopenharmony_ci{ 12818c2ecf20Sopenharmony_ci struct drm_syncobj_array *args = data; 12828c2ecf20Sopenharmony_ci struct drm_syncobj **syncobjs; 12838c2ecf20Sopenharmony_ci uint32_t i; 12848c2ecf20Sopenharmony_ci int ret; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ)) 12878c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci if (args->pad != 0) 12908c2ecf20Sopenharmony_ci return -EINVAL; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci if (args->count_handles == 0) 12938c2ecf20Sopenharmony_ci return -EINVAL; 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci ret = drm_syncobj_array_find(file_private, 12968c2ecf20Sopenharmony_ci u64_to_user_ptr(args->handles), 12978c2ecf20Sopenharmony_ci args->count_handles, 12988c2ecf20Sopenharmony_ci &syncobjs); 12998c2ecf20Sopenharmony_ci if (ret < 0) 13008c2ecf20Sopenharmony_ci return ret; 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci for (i = 0; i < args->count_handles; i++) 13038c2ecf20Sopenharmony_ci drm_syncobj_replace_fence(syncobjs[i], NULL); 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci drm_syncobj_array_free(syncobjs, args->count_handles); 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci return 0; 13088c2ecf20Sopenharmony_ci} 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ciint 13118c2ecf20Sopenharmony_cidrm_syncobj_signal_ioctl(struct drm_device *dev, void *data, 13128c2ecf20Sopenharmony_ci struct drm_file *file_private) 13138c2ecf20Sopenharmony_ci{ 13148c2ecf20Sopenharmony_ci struct drm_syncobj_array *args = data; 13158c2ecf20Sopenharmony_ci struct drm_syncobj **syncobjs; 13168c2ecf20Sopenharmony_ci uint32_t i; 13178c2ecf20Sopenharmony_ci int ret; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ)) 13208c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci if (args->pad != 0) 13238c2ecf20Sopenharmony_ci return -EINVAL; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci if (args->count_handles == 0) 13268c2ecf20Sopenharmony_ci return -EINVAL; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci ret = drm_syncobj_array_find(file_private, 13298c2ecf20Sopenharmony_ci u64_to_user_ptr(args->handles), 13308c2ecf20Sopenharmony_ci args->count_handles, 13318c2ecf20Sopenharmony_ci &syncobjs); 13328c2ecf20Sopenharmony_ci if (ret < 0) 13338c2ecf20Sopenharmony_ci return ret; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci for (i = 0; i < args->count_handles; i++) 13368c2ecf20Sopenharmony_ci drm_syncobj_assign_null_handle(syncobjs[i]); 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci drm_syncobj_array_free(syncobjs, args->count_handles); 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci return ret; 13418c2ecf20Sopenharmony_ci} 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ciint 13448c2ecf20Sopenharmony_cidrm_syncobj_timeline_signal_ioctl(struct drm_device *dev, void *data, 13458c2ecf20Sopenharmony_ci struct drm_file *file_private) 13468c2ecf20Sopenharmony_ci{ 13478c2ecf20Sopenharmony_ci struct drm_syncobj_timeline_array *args = data; 13488c2ecf20Sopenharmony_ci struct drm_syncobj **syncobjs; 13498c2ecf20Sopenharmony_ci struct dma_fence_chain **chains; 13508c2ecf20Sopenharmony_ci uint64_t *points; 13518c2ecf20Sopenharmony_ci uint32_t i, j; 13528c2ecf20Sopenharmony_ci int ret; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE)) 13558c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci if (args->flags != 0) 13588c2ecf20Sopenharmony_ci return -EINVAL; 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci if (args->count_handles == 0) 13618c2ecf20Sopenharmony_ci return -EINVAL; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci ret = drm_syncobj_array_find(file_private, 13648c2ecf20Sopenharmony_ci u64_to_user_ptr(args->handles), 13658c2ecf20Sopenharmony_ci args->count_handles, 13668c2ecf20Sopenharmony_ci &syncobjs); 13678c2ecf20Sopenharmony_ci if (ret < 0) 13688c2ecf20Sopenharmony_ci return ret; 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci points = kmalloc_array(args->count_handles, sizeof(*points), 13718c2ecf20Sopenharmony_ci GFP_KERNEL); 13728c2ecf20Sopenharmony_ci if (!points) { 13738c2ecf20Sopenharmony_ci ret = -ENOMEM; 13748c2ecf20Sopenharmony_ci goto out; 13758c2ecf20Sopenharmony_ci } 13768c2ecf20Sopenharmony_ci if (!u64_to_user_ptr(args->points)) { 13778c2ecf20Sopenharmony_ci memset(points, 0, args->count_handles * sizeof(uint64_t)); 13788c2ecf20Sopenharmony_ci } else if (copy_from_user(points, u64_to_user_ptr(args->points), 13798c2ecf20Sopenharmony_ci sizeof(uint64_t) * args->count_handles)) { 13808c2ecf20Sopenharmony_ci ret = -EFAULT; 13818c2ecf20Sopenharmony_ci goto err_points; 13828c2ecf20Sopenharmony_ci } 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci chains = kmalloc_array(args->count_handles, sizeof(void *), GFP_KERNEL); 13858c2ecf20Sopenharmony_ci if (!chains) { 13868c2ecf20Sopenharmony_ci ret = -ENOMEM; 13878c2ecf20Sopenharmony_ci goto err_points; 13888c2ecf20Sopenharmony_ci } 13898c2ecf20Sopenharmony_ci for (i = 0; i < args->count_handles; i++) { 13908c2ecf20Sopenharmony_ci chains[i] = kzalloc(sizeof(struct dma_fence_chain), GFP_KERNEL); 13918c2ecf20Sopenharmony_ci if (!chains[i]) { 13928c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) 13938c2ecf20Sopenharmony_ci kfree(chains[j]); 13948c2ecf20Sopenharmony_ci ret = -ENOMEM; 13958c2ecf20Sopenharmony_ci goto err_chains; 13968c2ecf20Sopenharmony_ci } 13978c2ecf20Sopenharmony_ci } 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci for (i = 0; i < args->count_handles; i++) { 14008c2ecf20Sopenharmony_ci struct dma_fence *fence = dma_fence_get_stub(); 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci drm_syncobj_add_point(syncobjs[i], chains[i], 14038c2ecf20Sopenharmony_ci fence, points[i]); 14048c2ecf20Sopenharmony_ci dma_fence_put(fence); 14058c2ecf20Sopenharmony_ci } 14068c2ecf20Sopenharmony_cierr_chains: 14078c2ecf20Sopenharmony_ci kfree(chains); 14088c2ecf20Sopenharmony_cierr_points: 14098c2ecf20Sopenharmony_ci kfree(points); 14108c2ecf20Sopenharmony_ciout: 14118c2ecf20Sopenharmony_ci drm_syncobj_array_free(syncobjs, args->count_handles); 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci return ret; 14148c2ecf20Sopenharmony_ci} 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ciint drm_syncobj_query_ioctl(struct drm_device *dev, void *data, 14178c2ecf20Sopenharmony_ci struct drm_file *file_private) 14188c2ecf20Sopenharmony_ci{ 14198c2ecf20Sopenharmony_ci struct drm_syncobj_timeline_array *args = data; 14208c2ecf20Sopenharmony_ci struct drm_syncobj **syncobjs; 14218c2ecf20Sopenharmony_ci uint64_t __user *points = u64_to_user_ptr(args->points); 14228c2ecf20Sopenharmony_ci uint32_t i; 14238c2ecf20Sopenharmony_ci int ret; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE)) 14268c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci if (args->flags & ~DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED) 14298c2ecf20Sopenharmony_ci return -EINVAL; 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci if (args->count_handles == 0) 14328c2ecf20Sopenharmony_ci return -EINVAL; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci ret = drm_syncobj_array_find(file_private, 14358c2ecf20Sopenharmony_ci u64_to_user_ptr(args->handles), 14368c2ecf20Sopenharmony_ci args->count_handles, 14378c2ecf20Sopenharmony_ci &syncobjs); 14388c2ecf20Sopenharmony_ci if (ret < 0) 14398c2ecf20Sopenharmony_ci return ret; 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci for (i = 0; i < args->count_handles; i++) { 14428c2ecf20Sopenharmony_ci struct dma_fence_chain *chain; 14438c2ecf20Sopenharmony_ci struct dma_fence *fence; 14448c2ecf20Sopenharmony_ci uint64_t point; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci fence = drm_syncobj_fence_get(syncobjs[i]); 14478c2ecf20Sopenharmony_ci chain = to_dma_fence_chain(fence); 14488c2ecf20Sopenharmony_ci if (chain) { 14498c2ecf20Sopenharmony_ci struct dma_fence *iter, *last_signaled = 14508c2ecf20Sopenharmony_ci dma_fence_get(fence); 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci if (args->flags & 14538c2ecf20Sopenharmony_ci DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED) { 14548c2ecf20Sopenharmony_ci point = fence->seqno; 14558c2ecf20Sopenharmony_ci } else { 14568c2ecf20Sopenharmony_ci dma_fence_chain_for_each(iter, fence) { 14578c2ecf20Sopenharmony_ci if (iter->context != fence->context) { 14588c2ecf20Sopenharmony_ci dma_fence_put(iter); 14598c2ecf20Sopenharmony_ci /* It is most likely that timeline has 14608c2ecf20Sopenharmony_ci * unorder points. */ 14618c2ecf20Sopenharmony_ci break; 14628c2ecf20Sopenharmony_ci } 14638c2ecf20Sopenharmony_ci dma_fence_put(last_signaled); 14648c2ecf20Sopenharmony_ci last_signaled = dma_fence_get(iter); 14658c2ecf20Sopenharmony_ci } 14668c2ecf20Sopenharmony_ci point = dma_fence_is_signaled(last_signaled) ? 14678c2ecf20Sopenharmony_ci last_signaled->seqno : 14688c2ecf20Sopenharmony_ci to_dma_fence_chain(last_signaled)->prev_seqno; 14698c2ecf20Sopenharmony_ci } 14708c2ecf20Sopenharmony_ci dma_fence_put(last_signaled); 14718c2ecf20Sopenharmony_ci } else { 14728c2ecf20Sopenharmony_ci point = 0; 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci dma_fence_put(fence); 14758c2ecf20Sopenharmony_ci ret = copy_to_user(&points[i], &point, sizeof(uint64_t)); 14768c2ecf20Sopenharmony_ci ret = ret ? -EFAULT : 0; 14778c2ecf20Sopenharmony_ci if (ret) 14788c2ecf20Sopenharmony_ci break; 14798c2ecf20Sopenharmony_ci } 14808c2ecf20Sopenharmony_ci drm_syncobj_array_free(syncobjs, args->count_handles); 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci return ret; 14838c2ecf20Sopenharmony_ci} 1484