LCOV - code coverage report
Current view: top level - drivers/gpu/drm - drm_syncobj.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 511 0.0 %
Date: 2022-12-09 01:23:36 Functions: 0 41 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright 2017 Red Hat
       3             :  * Parts ported from amdgpu (fence wait code).
       4             :  * Copyright 2016 Advanced Micro Devices, Inc.
       5             :  *
       6             :  * Permission is hereby granted, free of charge, to any person obtaining a
       7             :  * copy of this software and associated documentation files (the "Software"),
       8             :  * to deal in the Software without restriction, including without limitation
       9             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      10             :  * and/or sell copies of the Software, and to permit persons to whom the
      11             :  * Software is furnished to do so, subject to the following conditions:
      12             :  *
      13             :  * The above copyright notice and this permission notice (including the next
      14             :  * paragraph) shall be included in all copies or substantial portions of the
      15             :  * Software.
      16             :  *
      17             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      18             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      19             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
      20             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      21             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      22             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
      23             :  * IN THE SOFTWARE.
      24             :  *
      25             :  * Authors:
      26             :  *
      27             :  */
      28             : 
      29             : /**
      30             :  * DOC: Overview
      31             :  *
      32             :  * DRM synchronisation objects (syncobj, see struct &drm_syncobj) provide a
      33             :  * container for a synchronization primitive which can be used by userspace
      34             :  * to explicitly synchronize GPU commands, can be shared between userspace
      35             :  * processes, and can be shared between different DRM drivers.
      36             :  * Their primary use-case is to implement Vulkan fences and semaphores.
      37             :  * The syncobj userspace API provides ioctls for several operations:
      38             :  *
      39             :  *  - Creation and destruction of syncobjs
      40             :  *  - Import and export of syncobjs to/from a syncobj file descriptor
      41             :  *  - Import and export a syncobj's underlying fence to/from a sync file
      42             :  *  - Reset a syncobj (set its fence to NULL)
      43             :  *  - Signal a syncobj (set a trivially signaled fence)
      44             :  *  - Wait for a syncobj's fence to appear and be signaled
      45             :  *
      46             :  * The syncobj userspace API also provides operations to manipulate a syncobj
      47             :  * in terms of a timeline of struct &dma_fence_chain rather than a single
      48             :  * struct &dma_fence, through the following operations:
      49             :  *
      50             :  *   - Signal a given point on the timeline
      51             :  *   - Wait for a given point to appear and/or be signaled
      52             :  *   - Import and export from/to a given point of a timeline
      53             :  *
      54             :  * At it's core, a syncobj is simply a wrapper around a pointer to a struct
      55             :  * &dma_fence which may be NULL.
      56             :  * When a syncobj is first created, its pointer is either NULL or a pointer
      57             :  * to an already signaled fence depending on whether the
      58             :  * &DRM_SYNCOBJ_CREATE_SIGNALED flag is passed to
      59             :  * &DRM_IOCTL_SYNCOBJ_CREATE.
      60             :  *
      61             :  * If the syncobj is considered as a binary (its state is either signaled or
      62             :  * unsignaled) primitive, when GPU work is enqueued in a DRM driver to signal
      63             :  * the syncobj, the syncobj's fence is replaced with a fence which will be
      64             :  * signaled by the completion of that work.
      65             :  * If the syncobj is considered as a timeline primitive, when GPU work is
      66             :  * enqueued in a DRM driver to signal the a given point of the syncobj, a new
      67             :  * struct &dma_fence_chain pointing to the DRM driver's fence and also
      68             :  * pointing to the previous fence that was in the syncobj. The new struct
      69             :  * &dma_fence_chain fence replace the syncobj's fence and will be signaled by
      70             :  * completion of the DRM driver's work and also any work associated with the
      71             :  * fence previously in the syncobj.
      72             :  *
      73             :  * When GPU work which waits on a syncobj is enqueued in a DRM driver, at the
      74             :  * time the work is enqueued, it waits on the syncobj's fence before
      75             :  * submitting the work to hardware. That fence is either :
      76             :  *
      77             :  *    - The syncobj's current fence if the syncobj is considered as a binary
      78             :  *      primitive.
      79             :  *    - The struct &dma_fence associated with a given point if the syncobj is
      80             :  *      considered as a timeline primitive.
      81             :  *
      82             :  * If the syncobj's fence is NULL or not present in the syncobj's timeline,
      83             :  * the enqueue operation is expected to fail.
      84             :  *
      85             :  * With binary syncobj, all manipulation of the syncobjs's fence happens in
      86             :  * terms of the current fence at the time the ioctl is called by userspace
      87             :  * regardless of whether that operation is an immediate host-side operation
      88             :  * (signal or reset) or or an operation which is enqueued in some driver
      89             :  * queue. &DRM_IOCTL_SYNCOBJ_RESET and &DRM_IOCTL_SYNCOBJ_SIGNAL can be used
      90             :  * to manipulate a syncobj from the host by resetting its pointer to NULL or
      91             :  * setting its pointer to a fence which is already signaled.
      92             :  *
      93             :  * With a timeline syncobj, all manipulation of the synobj's fence happens in
      94             :  * terms of a u64 value referring to point in the timeline. See
      95             :  * dma_fence_chain_find_seqno() to see how a given point is found in the
      96             :  * timeline.
      97             :  *
      98             :  * Note that applications should be careful to always use timeline set of
      99             :  * ioctl() when dealing with syncobj considered as timeline. Using a binary
     100             :  * set of ioctl() with a syncobj considered as timeline could result incorrect
     101             :  * synchronization. The use of binary syncobj is supported through the
     102             :  * timeline set of ioctl() by using a point value of 0, this will reproduce
     103             :  * the behavior of the binary set of ioctl() (for example replace the
     104             :  * syncobj's fence when signaling).
     105             :  *
     106             :  *
     107             :  * Host-side wait on syncobjs
     108             :  * --------------------------
     109             :  *
     110             :  * &DRM_IOCTL_SYNCOBJ_WAIT takes an array of syncobj handles and does a
     111             :  * host-side wait on all of the syncobj fences simultaneously.
     112             :  * If &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL is set, the wait ioctl will wait on
     113             :  * all of the syncobj fences to be signaled before it returns.
     114             :  * Otherwise, it returns once at least one syncobj fence has been signaled
     115             :  * and the index of a signaled fence is written back to the client.
     116             :  *
     117             :  * Unlike the enqueued GPU work dependencies which fail if they see a NULL
     118             :  * fence in a syncobj, if &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT is set,
     119             :  * the host-side wait will first wait for the syncobj to receive a non-NULL
     120             :  * fence and then wait on that fence.
     121             :  * If &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT is not set and any one of the
     122             :  * syncobjs in the array has a NULL fence, -EINVAL will be returned.
     123             :  * Assuming the syncobj starts off with a NULL fence, this allows a client
     124             :  * to do a host wait in one thread (or process) which waits on GPU work
     125             :  * submitted in another thread (or process) without having to manually
     126             :  * synchronize between the two.
     127             :  * This requirement is inherited from the Vulkan fence API.
     128             :  *
     129             :  * Similarly, &DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT takes an array of syncobj
     130             :  * handles as well as an array of u64 points and does a host-side wait on all
     131             :  * of syncobj fences at the given points simultaneously.
     132             :  *
     133             :  * &DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT also adds the ability to wait for a given
     134             :  * fence to materialize on the timeline without waiting for the fence to be
     135             :  * signaled by using the &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE flag. This
     136             :  * requirement is inherited from the wait-before-signal behavior required by
     137             :  * the Vulkan timeline semaphore API.
     138             :  *
     139             :  *
     140             :  * Import/export of syncobjs
     141             :  * -------------------------
     142             :  *
     143             :  * &DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE and &DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD
     144             :  * provide two mechanisms for import/export of syncobjs.
     145             :  *
     146             :  * The first lets the client import or export an entire syncobj to a file
     147             :  * descriptor.
     148             :  * These fd's are opaque and have no other use case, except passing the
     149             :  * syncobj between processes.
     150             :  * All exported file descriptors and any syncobj handles created as a
     151             :  * result of importing those file descriptors own a reference to the
     152             :  * same underlying struct &drm_syncobj and the syncobj can be used
     153             :  * persistently across all the processes with which it is shared.
     154             :  * The syncobj is freed only once the last reference is dropped.
     155             :  * Unlike dma-buf, importing a syncobj creates a new handle (with its own
     156             :  * reference) for every import instead of de-duplicating.
     157             :  * The primary use-case of this persistent import/export is for shared
     158             :  * Vulkan fences and semaphores.
     159             :  *
     160             :  * The second import/export mechanism, which is indicated by
     161             :  * &DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE or
     162             :  * &DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE lets the client
     163             :  * import/export the syncobj's current fence from/to a &sync_file.
     164             :  * When a syncobj is exported to a sync file, that sync file wraps the
     165             :  * sycnobj's fence at the time of export and any later signal or reset
     166             :  * operations on the syncobj will not affect the exported sync file.
     167             :  * When a sync file is imported into a syncobj, the syncobj's fence is set
     168             :  * to the fence wrapped by that sync file.
     169             :  * Because sync files are immutable, resetting or signaling the syncobj
     170             :  * will not affect any sync files whose fences have been imported into the
     171             :  * syncobj.
     172             :  *
     173             :  *
     174             :  * Import/export of timeline points in timeline syncobjs
     175             :  * -----------------------------------------------------
     176             :  *
     177             :  * &DRM_IOCTL_SYNCOBJ_TRANSFER provides a mechanism to transfer a struct
     178             :  * &dma_fence_chain of a syncobj at a given u64 point to another u64 point
     179             :  * into another syncobj.
     180             :  *
     181             :  * Note that if you want to transfer a struct &dma_fence_chain from a given
     182             :  * point on a timeline syncobj from/into a binary syncobj, you can use the
     183             :  * point 0 to mean take/replace the fence in the syncobj.
     184             :  */
     185             : 
     186             : #include <linux/anon_inodes.h>
     187             : #include <linux/file.h>
     188             : #include <linux/fs.h>
     189             : #include <linux/sched/signal.h>
     190             : #include <linux/sync_file.h>
     191             : #include <linux/uaccess.h>
     192             : 
     193             : #include <drm/drm.h>
     194             : #include <drm/drm_drv.h>
     195             : #include <drm/drm_file.h>
     196             : #include <drm/drm_gem.h>
     197             : #include <drm/drm_print.h>
     198             : #include <drm/drm_syncobj.h>
     199             : #include <drm/drm_utils.h>
     200             : 
     201             : #include "drm_internal.h"
     202             : 
     203             : struct syncobj_wait_entry {
     204             :         struct list_head node;
     205             :         struct task_struct *task;
     206             :         struct dma_fence *fence;
     207             :         struct dma_fence_cb fence_cb;
     208             :         u64    point;
     209             : };
     210             : 
     211             : static void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj,
     212             :                                       struct syncobj_wait_entry *wait);
     213             : 
     214             : /**
     215             :  * drm_syncobj_find - lookup and reference a sync object.
     216             :  * @file_private: drm file private pointer
     217             :  * @handle: sync object handle to lookup.
     218             :  *
     219             :  * Returns a reference to the syncobj pointed to by handle or NULL. The
     220             :  * reference must be released by calling drm_syncobj_put().
     221             :  */
     222           0 : struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private,
     223             :                                      u32 handle)
     224             : {
     225             :         struct drm_syncobj *syncobj;
     226             : 
     227           0 :         spin_lock(&file_private->syncobj_table_lock);
     228             : 
     229             :         /* Check if we currently have a reference on the object */
     230           0 :         syncobj = idr_find(&file_private->syncobj_idr, handle);
     231           0 :         if (syncobj)
     232             :                 drm_syncobj_get(syncobj);
     233             : 
     234           0 :         spin_unlock(&file_private->syncobj_table_lock);
     235             : 
     236           0 :         return syncobj;
     237             : }
     238             : EXPORT_SYMBOL(drm_syncobj_find);
     239             : 
     240           0 : static void drm_syncobj_fence_add_wait(struct drm_syncobj *syncobj,
     241             :                                        struct syncobj_wait_entry *wait)
     242             : {
     243             :         struct dma_fence *fence;
     244             : 
     245           0 :         if (wait->fence)
     246           0 :                 return;
     247             : 
     248           0 :         spin_lock(&syncobj->lock);
     249             :         /* We've already tried once to get a fence and failed.  Now that we
     250             :          * have the lock, try one more time just to be sure we don't add a
     251             :          * callback when a fence has already been set.
     252             :          */
     253           0 :         fence = dma_fence_get(rcu_dereference_protected(syncobj->fence, 1));
     254           0 :         if (!fence || dma_fence_chain_find_seqno(&fence, wait->point)) {
     255           0 :                 dma_fence_put(fence);
     256           0 :                 list_add_tail(&wait->node, &syncobj->cb_list);
     257           0 :         } else if (!fence) {
     258           0 :                 wait->fence = dma_fence_get_stub();
     259             :         } else {
     260           0 :                 wait->fence = fence;
     261             :         }
     262           0 :         spin_unlock(&syncobj->lock);
     263             : }
     264             : 
     265             : static void drm_syncobj_remove_wait(struct drm_syncobj *syncobj,
     266             :                                     struct syncobj_wait_entry *wait)
     267             : {
     268           0 :         if (!wait->node.next)
     269             :                 return;
     270             : 
     271           0 :         spin_lock(&syncobj->lock);
     272           0 :         list_del_init(&wait->node);
     273           0 :         spin_unlock(&syncobj->lock);
     274             : }
     275             : 
     276             : /**
     277             :  * drm_syncobj_add_point - add new timeline point to the syncobj
     278             :  * @syncobj: sync object to add timeline point do
     279             :  * @chain: chain node to use to add the point
     280             :  * @fence: fence to encapsulate in the chain node
     281             :  * @point: sequence number to use for the point
     282             :  *
     283             :  * Add the chain node as new timeline point to the syncobj.
     284             :  */
     285           0 : void drm_syncobj_add_point(struct drm_syncobj *syncobj,
     286             :                            struct dma_fence_chain *chain,
     287             :                            struct dma_fence *fence,
     288             :                            uint64_t point)
     289             : {
     290             :         struct syncobj_wait_entry *cur, *tmp;
     291             :         struct dma_fence *prev;
     292             : 
     293           0 :         dma_fence_get(fence);
     294             : 
     295           0 :         spin_lock(&syncobj->lock);
     296             : 
     297           0 :         prev = drm_syncobj_fence_get(syncobj);
     298             :         /* You are adding an unorder point to timeline, which could cause payload returned from query_ioctl is 0! */
     299           0 :         if (prev && prev->seqno >= point)
     300           0 :                 DRM_DEBUG("You are adding an unorder point to timeline!\n");
     301           0 :         dma_fence_chain_init(chain, prev, fence, point);
     302           0 :         rcu_assign_pointer(syncobj->fence, &chain->base);
     303             : 
     304           0 :         list_for_each_entry_safe(cur, tmp, &syncobj->cb_list, node)
     305           0 :                 syncobj_wait_syncobj_func(syncobj, cur);
     306           0 :         spin_unlock(&syncobj->lock);
     307             : 
     308             :         /* Walk the chain once to trigger garbage collection */
     309           0 :         dma_fence_chain_for_each(fence, prev);
     310           0 :         dma_fence_put(prev);
     311           0 : }
     312             : EXPORT_SYMBOL(drm_syncobj_add_point);
     313             : 
     314             : /**
     315             :  * drm_syncobj_replace_fence - replace fence in a sync object.
     316             :  * @syncobj: Sync object to replace fence in
     317             :  * @fence: fence to install in sync file.
     318             :  *
     319             :  * This replaces the fence on a sync object.
     320             :  */
     321           0 : void drm_syncobj_replace_fence(struct drm_syncobj *syncobj,
     322             :                                struct dma_fence *fence)
     323             : {
     324             :         struct dma_fence *old_fence;
     325             :         struct syncobj_wait_entry *cur, *tmp;
     326             : 
     327           0 :         if (fence)
     328             :                 dma_fence_get(fence);
     329             : 
     330           0 :         spin_lock(&syncobj->lock);
     331             : 
     332           0 :         old_fence = rcu_dereference_protected(syncobj->fence,
     333             :                                               lockdep_is_held(&syncobj->lock));
     334           0 :         rcu_assign_pointer(syncobj->fence, fence);
     335             : 
     336           0 :         if (fence != old_fence) {
     337           0 :                 list_for_each_entry_safe(cur, tmp, &syncobj->cb_list, node)
     338           0 :                         syncobj_wait_syncobj_func(syncobj, cur);
     339             :         }
     340             : 
     341           0 :         spin_unlock(&syncobj->lock);
     342             : 
     343           0 :         dma_fence_put(old_fence);
     344           0 : }
     345             : EXPORT_SYMBOL(drm_syncobj_replace_fence);
     346             : 
     347             : /**
     348             :  * drm_syncobj_assign_null_handle - assign a stub fence to the sync object
     349             :  * @syncobj: sync object to assign the fence on
     350             :  *
     351             :  * Assign a already signaled stub fence to the sync object.
     352             :  */
     353           0 : static int drm_syncobj_assign_null_handle(struct drm_syncobj *syncobj)
     354             : {
     355           0 :         struct dma_fence *fence = dma_fence_allocate_private_stub();
     356             : 
     357           0 :         if (IS_ERR(fence))
     358           0 :                 return PTR_ERR(fence);
     359             : 
     360           0 :         drm_syncobj_replace_fence(syncobj, fence);
     361             :         dma_fence_put(fence);
     362             :         return 0;
     363             : }
     364             : 
     365             : /* 5s default for wait submission */
     366             : #define DRM_SYNCOBJ_WAIT_FOR_SUBMIT_TIMEOUT 5000000000ULL
     367             : /**
     368             :  * drm_syncobj_find_fence - lookup and reference the fence in a sync object
     369             :  * @file_private: drm file private pointer
     370             :  * @handle: sync object handle to lookup.
     371             :  * @point: timeline point
     372             :  * @flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or not
     373             :  * @fence: out parameter for the fence
     374             :  *
     375             :  * This is just a convenience function that combines drm_syncobj_find() and
     376             :  * drm_syncobj_fence_get().
     377             :  *
     378             :  * Returns 0 on success or a negative error value on failure. On success @fence
     379             :  * contains a reference to the fence, which must be released by calling
     380             :  * dma_fence_put().
     381             :  */
     382           0 : int drm_syncobj_find_fence(struct drm_file *file_private,
     383             :                            u32 handle, u64 point, u64 flags,
     384             :                            struct dma_fence **fence)
     385             : {
     386           0 :         struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);
     387             :         struct syncobj_wait_entry wait;
     388           0 :         u64 timeout = nsecs_to_jiffies64(DRM_SYNCOBJ_WAIT_FOR_SUBMIT_TIMEOUT);
     389             :         int ret;
     390             : 
     391           0 :         if (!syncobj)
     392             :                 return -ENOENT;
     393             : 
     394             :         /* Waiting for userspace with locks help is illegal cause that can
     395             :          * trivial deadlock with page faults for example. Make lockdep complain
     396             :          * about it early on.
     397             :          */
     398           0 :         if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) {
     399             :                 might_sleep();
     400             :                 lockdep_assert_none_held_once();
     401             :         }
     402             : 
     403           0 :         *fence = drm_syncobj_fence_get(syncobj);
     404             : 
     405           0 :         if (*fence) {
     406           0 :                 ret = dma_fence_chain_find_seqno(fence, point);
     407           0 :                 if (!ret) {
     408             :                         /* If the requested seqno is already signaled
     409             :                          * drm_syncobj_find_fence may return a NULL
     410             :                          * fence. To make sure the recipient gets
     411             :                          * signalled, use a new fence instead.
     412             :                          */
     413           0 :                         if (!*fence)
     414           0 :                                 *fence = dma_fence_get_stub();
     415             : 
     416             :                         goto out;
     417             :                 }
     418           0 :                 dma_fence_put(*fence);
     419             :         } else {
     420             :                 ret = -EINVAL;
     421             :         }
     422             : 
     423           0 :         if (!(flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT))
     424             :                 goto out;
     425             : 
     426           0 :         memset(&wait, 0, sizeof(wait));
     427           0 :         wait.task = current;
     428           0 :         wait.point = point;
     429           0 :         drm_syncobj_fence_add_wait(syncobj, &wait);
     430             : 
     431             :         do {
     432           0 :                 set_current_state(TASK_INTERRUPTIBLE);
     433           0 :                 if (wait.fence) {
     434             :                         ret = 0;
     435             :                         break;
     436             :                 }
     437           0 :                 if (timeout == 0) {
     438             :                         ret = -ETIME;
     439             :                         break;
     440             :                 }
     441             : 
     442           0 :                 if (signal_pending(current)) {
     443             :                         ret = -ERESTARTSYS;
     444             :                         break;
     445             :                 }
     446             : 
     447           0 :                 timeout = schedule_timeout(timeout);
     448             :         } while (1);
     449             : 
     450           0 :         __set_current_state(TASK_RUNNING);
     451           0 :         *fence = wait.fence;
     452             : 
     453           0 :         if (wait.node.next)
     454           0 :                 drm_syncobj_remove_wait(syncobj, &wait);
     455             : 
     456             : out:
     457           0 :         drm_syncobj_put(syncobj);
     458             : 
     459           0 :         return ret;
     460             : }
     461             : EXPORT_SYMBOL(drm_syncobj_find_fence);
     462             : 
     463             : /**
     464             :  * drm_syncobj_free - free a sync object.
     465             :  * @kref: kref to free.
     466             :  *
     467             :  * Only to be called from kref_put in drm_syncobj_put.
     468             :  */
     469           0 : void drm_syncobj_free(struct kref *kref)
     470             : {
     471           0 :         struct drm_syncobj *syncobj = container_of(kref,
     472             :                                                    struct drm_syncobj,
     473             :                                                    refcount);
     474           0 :         drm_syncobj_replace_fence(syncobj, NULL);
     475           0 :         kfree(syncobj);
     476           0 : }
     477             : EXPORT_SYMBOL(drm_syncobj_free);
     478             : 
     479             : /**
     480             :  * drm_syncobj_create - create a new syncobj
     481             :  * @out_syncobj: returned syncobj
     482             :  * @flags: DRM_SYNCOBJ_* flags
     483             :  * @fence: if non-NULL, the syncobj will represent this fence
     484             :  *
     485             :  * This is the first function to create a sync object. After creating, drivers
     486             :  * probably want to make it available to userspace, either through
     487             :  * drm_syncobj_get_handle() or drm_syncobj_get_fd().
     488             :  *
     489             :  * Returns 0 on success or a negative error value on failure.
     490             :  */
     491           0 : int drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags,
     492             :                        struct dma_fence *fence)
     493             : {
     494             :         int ret;
     495             :         struct drm_syncobj *syncobj;
     496             : 
     497           0 :         syncobj = kzalloc(sizeof(struct drm_syncobj), GFP_KERNEL);
     498           0 :         if (!syncobj)
     499             :                 return -ENOMEM;
     500             : 
     501           0 :         kref_init(&syncobj->refcount);
     502           0 :         INIT_LIST_HEAD(&syncobj->cb_list);
     503           0 :         spin_lock_init(&syncobj->lock);
     504             : 
     505           0 :         if (flags & DRM_SYNCOBJ_CREATE_SIGNALED) {
     506           0 :                 ret = drm_syncobj_assign_null_handle(syncobj);
     507           0 :                 if (ret < 0) {
     508           0 :                         drm_syncobj_put(syncobj);
     509           0 :                         return ret;
     510             :                 }
     511             :         }
     512             : 
     513           0 :         if (fence)
     514           0 :                 drm_syncobj_replace_fence(syncobj, fence);
     515             : 
     516           0 :         *out_syncobj = syncobj;
     517           0 :         return 0;
     518             : }
     519             : EXPORT_SYMBOL(drm_syncobj_create);
     520             : 
     521             : /**
     522             :  * drm_syncobj_get_handle - get a handle from a syncobj
     523             :  * @file_private: drm file private pointer
     524             :  * @syncobj: Sync object to export
     525             :  * @handle: out parameter with the new handle
     526             :  *
     527             :  * Exports a sync object created with drm_syncobj_create() as a handle on
     528             :  * @file_private to userspace.
     529             :  *
     530             :  * Returns 0 on success or a negative error value on failure.
     531             :  */
     532           0 : int drm_syncobj_get_handle(struct drm_file *file_private,
     533             :                            struct drm_syncobj *syncobj, u32 *handle)
     534             : {
     535             :         int ret;
     536             : 
     537             :         /* take a reference to put in the idr */
     538           0 :         drm_syncobj_get(syncobj);
     539             : 
     540           0 :         idr_preload(GFP_KERNEL);
     541           0 :         spin_lock(&file_private->syncobj_table_lock);
     542           0 :         ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT);
     543           0 :         spin_unlock(&file_private->syncobj_table_lock);
     544             : 
     545             :         idr_preload_end();
     546             : 
     547           0 :         if (ret < 0) {
     548           0 :                 drm_syncobj_put(syncobj);
     549           0 :                 return ret;
     550             :         }
     551             : 
     552           0 :         *handle = ret;
     553           0 :         return 0;
     554             : }
     555             : EXPORT_SYMBOL(drm_syncobj_get_handle);
     556             : 
     557           0 : static int drm_syncobj_create_as_handle(struct drm_file *file_private,
     558             :                                         u32 *handle, uint32_t flags)
     559             : {
     560             :         int ret;
     561             :         struct drm_syncobj *syncobj;
     562             : 
     563           0 :         ret = drm_syncobj_create(&syncobj, flags, NULL);
     564           0 :         if (ret)
     565             :                 return ret;
     566             : 
     567           0 :         ret = drm_syncobj_get_handle(file_private, syncobj, handle);
     568           0 :         drm_syncobj_put(syncobj);
     569           0 :         return ret;
     570             : }
     571             : 
     572           0 : static int drm_syncobj_destroy(struct drm_file *file_private,
     573             :                                u32 handle)
     574             : {
     575             :         struct drm_syncobj *syncobj;
     576             : 
     577           0 :         spin_lock(&file_private->syncobj_table_lock);
     578           0 :         syncobj = idr_remove(&file_private->syncobj_idr, handle);
     579           0 :         spin_unlock(&file_private->syncobj_table_lock);
     580             : 
     581           0 :         if (!syncobj)
     582             :                 return -EINVAL;
     583             : 
     584           0 :         drm_syncobj_put(syncobj);
     585           0 :         return 0;
     586             : }
     587             : 
     588           0 : static int drm_syncobj_file_release(struct inode *inode, struct file *file)
     589             : {
     590           0 :         struct drm_syncobj *syncobj = file->private_data;
     591             : 
     592           0 :         drm_syncobj_put(syncobj);
     593           0 :         return 0;
     594             : }
     595             : 
     596             : static const struct file_operations drm_syncobj_file_fops = {
     597             :         .release = drm_syncobj_file_release,
     598             : };
     599             : 
     600             : /**
     601             :  * drm_syncobj_get_fd - get a file descriptor from a syncobj
     602             :  * @syncobj: Sync object to export
     603             :  * @p_fd: out parameter with the new file descriptor
     604             :  *
     605             :  * Exports a sync object created with drm_syncobj_create() as a file descriptor.
     606             :  *
     607             :  * Returns 0 on success or a negative error value on failure.
     608             :  */
     609           0 : int drm_syncobj_get_fd(struct drm_syncobj *syncobj, int *p_fd)
     610             : {
     611             :         struct file *file;
     612             :         int fd;
     613             : 
     614           0 :         fd = get_unused_fd_flags(O_CLOEXEC);
     615           0 :         if (fd < 0)
     616             :                 return fd;
     617             : 
     618           0 :         file = anon_inode_getfile("syncobj_file",
     619             :                                   &drm_syncobj_file_fops,
     620             :                                   syncobj, 0);
     621           0 :         if (IS_ERR(file)) {
     622           0 :                 put_unused_fd(fd);
     623           0 :                 return PTR_ERR(file);
     624             :         }
     625             : 
     626           0 :         drm_syncobj_get(syncobj);
     627           0 :         fd_install(fd, file);
     628             : 
     629           0 :         *p_fd = fd;
     630           0 :         return 0;
     631             : }
     632             : EXPORT_SYMBOL(drm_syncobj_get_fd);
     633             : 
     634           0 : static int drm_syncobj_handle_to_fd(struct drm_file *file_private,
     635             :                                     u32 handle, int *p_fd)
     636             : {
     637           0 :         struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);
     638             :         int ret;
     639             : 
     640           0 :         if (!syncobj)
     641             :                 return -EINVAL;
     642             : 
     643           0 :         ret = drm_syncobj_get_fd(syncobj, p_fd);
     644           0 :         drm_syncobj_put(syncobj);
     645           0 :         return ret;
     646             : }
     647             : 
     648           0 : static int drm_syncobj_fd_to_handle(struct drm_file *file_private,
     649             :                                     int fd, u32 *handle)
     650             : {
     651             :         struct drm_syncobj *syncobj;
     652           0 :         struct fd f = fdget(fd);
     653             :         int ret;
     654             : 
     655           0 :         if (!f.file)
     656             :                 return -EINVAL;
     657             : 
     658           0 :         if (f.file->f_op != &drm_syncobj_file_fops) {
     659           0 :                 fdput(f);
     660             :                 return -EINVAL;
     661             :         }
     662             : 
     663             :         /* take a reference to put in the idr */
     664           0 :         syncobj = f.file->private_data;
     665           0 :         drm_syncobj_get(syncobj);
     666             : 
     667           0 :         idr_preload(GFP_KERNEL);
     668           0 :         spin_lock(&file_private->syncobj_table_lock);
     669           0 :         ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT);
     670           0 :         spin_unlock(&file_private->syncobj_table_lock);
     671             :         idr_preload_end();
     672             : 
     673           0 :         if (ret > 0) {
     674           0 :                 *handle = ret;
     675           0 :                 ret = 0;
     676             :         } else
     677             :                 drm_syncobj_put(syncobj);
     678             : 
     679           0 :         fdput(f);
     680             :         return ret;
     681             : }
     682             : 
     683           0 : static int drm_syncobj_import_sync_file_fence(struct drm_file *file_private,
     684             :                                               int fd, int handle)
     685             : {
     686           0 :         struct dma_fence *fence = sync_file_get_fence(fd);
     687             :         struct drm_syncobj *syncobj;
     688             : 
     689           0 :         if (!fence)
     690             :                 return -EINVAL;
     691             : 
     692           0 :         syncobj = drm_syncobj_find(file_private, handle);
     693           0 :         if (!syncobj) {
     694           0 :                 dma_fence_put(fence);
     695           0 :                 return -ENOENT;
     696             :         }
     697             : 
     698           0 :         drm_syncobj_replace_fence(syncobj, fence);
     699           0 :         dma_fence_put(fence);
     700           0 :         drm_syncobj_put(syncobj);
     701           0 :         return 0;
     702             : }
     703             : 
     704           0 : static int drm_syncobj_export_sync_file(struct drm_file *file_private,
     705             :                                         int handle, int *p_fd)
     706             : {
     707             :         int ret;
     708             :         struct dma_fence *fence;
     709             :         struct sync_file *sync_file;
     710           0 :         int fd = get_unused_fd_flags(O_CLOEXEC);
     711             : 
     712           0 :         if (fd < 0)
     713             :                 return fd;
     714             : 
     715           0 :         ret = drm_syncobj_find_fence(file_private, handle, 0, 0, &fence);
     716           0 :         if (ret)
     717             :                 goto err_put_fd;
     718             : 
     719           0 :         sync_file = sync_file_create(fence);
     720             : 
     721           0 :         dma_fence_put(fence);
     722             : 
     723           0 :         if (!sync_file) {
     724             :                 ret = -EINVAL;
     725             :                 goto err_put_fd;
     726             :         }
     727             : 
     728           0 :         fd_install(fd, sync_file->file);
     729             : 
     730           0 :         *p_fd = fd;
     731           0 :         return 0;
     732             : err_put_fd:
     733           0 :         put_unused_fd(fd);
     734           0 :         return ret;
     735             : }
     736             : /**
     737             :  * drm_syncobj_open - initializes syncobj file-private structures at devnode open time
     738             :  * @file_private: drm file-private structure to set up
     739             :  *
     740             :  * Called at device open time, sets up the structure for handling refcounting
     741             :  * of sync objects.
     742             :  */
     743             : void
     744           0 : drm_syncobj_open(struct drm_file *file_private)
     745             : {
     746           0 :         idr_init_base(&file_private->syncobj_idr, 1);
     747           0 :         spin_lock_init(&file_private->syncobj_table_lock);
     748           0 : }
     749             : 
     750             : static int
     751           0 : drm_syncobj_release_handle(int id, void *ptr, void *data)
     752             : {
     753           0 :         struct drm_syncobj *syncobj = ptr;
     754             : 
     755           0 :         drm_syncobj_put(syncobj);
     756           0 :         return 0;
     757             : }
     758             : 
     759             : /**
     760             :  * drm_syncobj_release - release file-private sync object resources
     761             :  * @file_private: drm file-private structure to clean up
     762             :  *
     763             :  * Called at close time when the filp is going away.
     764             :  *
     765             :  * Releases any remaining references on objects by this filp.
     766             :  */
     767             : void
     768           0 : drm_syncobj_release(struct drm_file *file_private)
     769             : {
     770           0 :         idr_for_each(&file_private->syncobj_idr,
     771             :                      &drm_syncobj_release_handle, file_private);
     772           0 :         idr_destroy(&file_private->syncobj_idr);
     773           0 : }
     774             : 
     775             : int
     776           0 : drm_syncobj_create_ioctl(struct drm_device *dev, void *data,
     777             :                          struct drm_file *file_private)
     778             : {
     779           0 :         struct drm_syncobj_create *args = data;
     780             : 
     781           0 :         if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
     782             :                 return -EOPNOTSUPP;
     783             : 
     784             :         /* no valid flags yet */
     785           0 :         if (args->flags & ~DRM_SYNCOBJ_CREATE_SIGNALED)
     786             :                 return -EINVAL;
     787             : 
     788           0 :         return drm_syncobj_create_as_handle(file_private,
     789           0 :                                             &args->handle, args->flags);
     790             : }
     791             : 
     792             : int
     793           0 : drm_syncobj_destroy_ioctl(struct drm_device *dev, void *data,
     794             :                           struct drm_file *file_private)
     795             : {
     796           0 :         struct drm_syncobj_destroy *args = data;
     797             : 
     798           0 :         if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
     799             :                 return -EOPNOTSUPP;
     800             : 
     801             :         /* make sure padding is empty */
     802           0 :         if (args->pad)
     803             :                 return -EINVAL;
     804           0 :         return drm_syncobj_destroy(file_private, args->handle);
     805             : }
     806             : 
     807             : int
     808           0 : drm_syncobj_handle_to_fd_ioctl(struct drm_device *dev, void *data,
     809             :                                    struct drm_file *file_private)
     810             : {
     811           0 :         struct drm_syncobj_handle *args = data;
     812             : 
     813           0 :         if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
     814             :                 return -EOPNOTSUPP;
     815             : 
     816           0 :         if (args->pad)
     817             :                 return -EINVAL;
     818             : 
     819           0 :         if (args->flags != 0 &&
     820             :             args->flags != DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE)
     821             :                 return -EINVAL;
     822             : 
     823           0 :         if (args->flags & DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE)
     824           0 :                 return drm_syncobj_export_sync_file(file_private, args->handle,
     825           0 :                                                     &args->fd);
     826             : 
     827           0 :         return drm_syncobj_handle_to_fd(file_private, args->handle,
     828           0 :                                         &args->fd);
     829             : }
     830             : 
     831             : int
     832           0 : drm_syncobj_fd_to_handle_ioctl(struct drm_device *dev, void *data,
     833             :                                    struct drm_file *file_private)
     834             : {
     835           0 :         struct drm_syncobj_handle *args = data;
     836             : 
     837           0 :         if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
     838             :                 return -EOPNOTSUPP;
     839             : 
     840           0 :         if (args->pad)
     841             :                 return -EINVAL;
     842             : 
     843           0 :         if (args->flags != 0 &&
     844             :             args->flags != DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE)
     845             :                 return -EINVAL;
     846             : 
     847           0 :         if (args->flags & DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE)
     848           0 :                 return drm_syncobj_import_sync_file_fence(file_private,
     849             :                                                           args->fd,
     850           0 :                                                           args->handle);
     851             : 
     852           0 :         return drm_syncobj_fd_to_handle(file_private, args->fd,
     853           0 :                                         &args->handle);
     854             : }
     855             : 
     856             : 
     857             : /*
     858             :  * Try to flatten a dma_fence_chain into a dma_fence_array so that it can be
     859             :  * added as timeline fence to a chain again.
     860             :  */
     861           0 : static int drm_syncobj_flatten_chain(struct dma_fence **f)
     862             : {
     863           0 :         struct dma_fence_chain *chain = to_dma_fence_chain(*f);
     864             :         struct dma_fence *tmp, **fences;
     865             :         struct dma_fence_array *array;
     866             :         unsigned int count;
     867             : 
     868           0 :         if (!chain)
     869             :                 return 0;
     870             : 
     871           0 :         count = 0;
     872           0 :         dma_fence_chain_for_each(tmp, &chain->base)
     873           0 :                 ++count;
     874             : 
     875           0 :         fences = kmalloc_array(count, sizeof(*fences), GFP_KERNEL);
     876           0 :         if (!fences)
     877             :                 return -ENOMEM;
     878             : 
     879           0 :         count = 0;
     880           0 :         dma_fence_chain_for_each(tmp, &chain->base)
     881           0 :                 fences[count++] = dma_fence_get(tmp);
     882             : 
     883           0 :         array = dma_fence_array_create(count, fences,
     884             :                                        dma_fence_context_alloc(1),
     885             :                                        1, false);
     886           0 :         if (!array)
     887             :                 goto free_fences;
     888             : 
     889           0 :         dma_fence_put(*f);
     890           0 :         *f = &array->base;
     891           0 :         return 0;
     892             : 
     893             : free_fences:
     894           0 :         while (count--)
     895           0 :                 dma_fence_put(fences[count]);
     896             : 
     897           0 :         kfree(fences);
     898           0 :         return -ENOMEM;
     899             : }
     900             : 
     901           0 : static int drm_syncobj_transfer_to_timeline(struct drm_file *file_private,
     902             :                                             struct drm_syncobj_transfer *args)
     903             : {
     904           0 :         struct drm_syncobj *timeline_syncobj = NULL;
     905             :         struct dma_fence_chain *chain;
     906             :         struct dma_fence *fence;
     907             :         int ret;
     908             : 
     909           0 :         timeline_syncobj = drm_syncobj_find(file_private, args->dst_handle);
     910           0 :         if (!timeline_syncobj) {
     911             :                 return -ENOENT;
     912             :         }
     913           0 :         ret = drm_syncobj_find_fence(file_private, args->src_handle,
     914           0 :                                      args->src_point, args->flags,
     915             :                                      &fence);
     916           0 :         if (ret)
     917             :                 goto err_put_timeline;
     918             : 
     919           0 :         ret = drm_syncobj_flatten_chain(&fence);
     920           0 :         if (ret)
     921             :                 goto err_free_fence;
     922             : 
     923           0 :         chain = dma_fence_chain_alloc();
     924           0 :         if (!chain) {
     925             :                 ret = -ENOMEM;
     926             :                 goto err_free_fence;
     927             :         }
     928             : 
     929           0 :         drm_syncobj_add_point(timeline_syncobj, chain, fence, args->dst_point);
     930             : err_free_fence:
     931           0 :         dma_fence_put(fence);
     932             : err_put_timeline:
     933           0 :         drm_syncobj_put(timeline_syncobj);
     934             : 
     935           0 :         return ret;
     936             : }
     937             : 
     938             : static int
     939           0 : drm_syncobj_transfer_to_binary(struct drm_file *file_private,
     940             :                                struct drm_syncobj_transfer *args)
     941             : {
     942           0 :         struct drm_syncobj *binary_syncobj = NULL;
     943             :         struct dma_fence *fence;
     944             :         int ret;
     945             : 
     946           0 :         binary_syncobj = drm_syncobj_find(file_private, args->dst_handle);
     947           0 :         if (!binary_syncobj)
     948             :                 return -ENOENT;
     949           0 :         ret = drm_syncobj_find_fence(file_private, args->src_handle,
     950           0 :                                      args->src_point, args->flags, &fence);
     951           0 :         if (ret)
     952             :                 goto err;
     953           0 :         drm_syncobj_replace_fence(binary_syncobj, fence);
     954           0 :         dma_fence_put(fence);
     955             : err:
     956           0 :         drm_syncobj_put(binary_syncobj);
     957             : 
     958           0 :         return ret;
     959             : }
     960             : int
     961           0 : drm_syncobj_transfer_ioctl(struct drm_device *dev, void *data,
     962             :                            struct drm_file *file_private)
     963             : {
     964           0 :         struct drm_syncobj_transfer *args = data;
     965             :         int ret;
     966             : 
     967           0 :         if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
     968             :                 return -EOPNOTSUPP;
     969             : 
     970           0 :         if (args->pad)
     971             :                 return -EINVAL;
     972             : 
     973           0 :         if (args->dst_point)
     974           0 :                 ret = drm_syncobj_transfer_to_timeline(file_private, args);
     975             :         else
     976           0 :                 ret = drm_syncobj_transfer_to_binary(file_private, args);
     977             : 
     978             :         return ret;
     979             : }
     980             : 
     981           0 : static void syncobj_wait_fence_func(struct dma_fence *fence,
     982             :                                     struct dma_fence_cb *cb)
     983             : {
     984           0 :         struct syncobj_wait_entry *wait =
     985           0 :                 container_of(cb, struct syncobj_wait_entry, fence_cb);
     986             : 
     987           0 :         wake_up_process(wait->task);
     988           0 : }
     989             : 
     990           0 : static void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj,
     991             :                                       struct syncobj_wait_entry *wait)
     992             : {
     993             :         struct dma_fence *fence;
     994             : 
     995             :         /* This happens inside the syncobj lock */
     996           0 :         fence = rcu_dereference_protected(syncobj->fence,
     997             :                                           lockdep_is_held(&syncobj->lock));
     998           0 :         dma_fence_get(fence);
     999           0 :         if (!fence || dma_fence_chain_find_seqno(&fence, wait->point)) {
    1000           0 :                 dma_fence_put(fence);
    1001           0 :                 return;
    1002           0 :         } else if (!fence) {
    1003           0 :                 wait->fence = dma_fence_get_stub();
    1004             :         } else {
    1005           0 :                 wait->fence = fence;
    1006             :         }
    1007             : 
    1008           0 :         wake_up_process(wait->task);
    1009           0 :         list_del_init(&wait->node);
    1010             : }
    1011             : 
    1012           0 : static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs,
    1013             :                                                   void __user *user_points,
    1014             :                                                   uint32_t count,
    1015             :                                                   uint32_t flags,
    1016             :                                                   signed long timeout,
    1017             :                                                   uint32_t *idx)
    1018             : {
    1019             :         struct syncobj_wait_entry *entries;
    1020             :         struct dma_fence *fence;
    1021             :         uint64_t *points;
    1022             :         uint32_t signaled_count, i;
    1023             : 
    1024           0 :         if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT)
    1025             :                 lockdep_assert_none_held_once();
    1026             : 
    1027           0 :         points = kmalloc_array(count, sizeof(*points), GFP_KERNEL);
    1028           0 :         if (points == NULL)
    1029             :                 return -ENOMEM;
    1030             : 
    1031           0 :         if (!user_points) {
    1032           0 :                 memset(points, 0, count * sizeof(uint64_t));
    1033             : 
    1034           0 :         } else if (copy_from_user(points, user_points,
    1035             :                                   sizeof(uint64_t) * count)) {
    1036             :                 timeout = -EFAULT;
    1037             :                 goto err_free_points;
    1038             :         }
    1039             : 
    1040           0 :         entries = kcalloc(count, sizeof(*entries), GFP_KERNEL);
    1041           0 :         if (!entries) {
    1042             :                 timeout = -ENOMEM;
    1043             :                 goto err_free_points;
    1044             :         }
    1045             :         /* Walk the list of sync objects and initialize entries.  We do
    1046             :          * this up-front so that we can properly return -EINVAL if there is
    1047             :          * a syncobj with a missing fence and then never have the chance of
    1048             :          * returning -EINVAL again.
    1049             :          */
    1050             :         signaled_count = 0;
    1051           0 :         for (i = 0; i < count; ++i) {
    1052             :                 struct dma_fence *fence;
    1053             : 
    1054           0 :                 entries[i].task = current;
    1055           0 :                 entries[i].point = points[i];
    1056           0 :                 fence = drm_syncobj_fence_get(syncobjs[i]);
    1057           0 :                 if (!fence || dma_fence_chain_find_seqno(&fence, points[i])) {
    1058           0 :                         dma_fence_put(fence);
    1059           0 :                         if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) {
    1060           0 :                                 continue;
    1061             :                         } else {
    1062           0 :                                 timeout = -EINVAL;
    1063           0 :                                 goto cleanup_entries;
    1064             :                         }
    1065             :                 }
    1066             : 
    1067           0 :                 if (fence)
    1068           0 :                         entries[i].fence = fence;
    1069             :                 else
    1070           0 :                         entries[i].fence = dma_fence_get_stub();
    1071             : 
    1072           0 :                 if ((flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE) ||
    1073           0 :                     dma_fence_is_signaled(entries[i].fence)) {
    1074           0 :                         if (signaled_count == 0 && idx)
    1075           0 :                                 *idx = i;
    1076           0 :                         signaled_count++;
    1077             :                 }
    1078             :         }
    1079             : 
    1080           0 :         if (signaled_count == count ||
    1081           0 :             (signaled_count > 0 &&
    1082           0 :              !(flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL)))
    1083             :                 goto cleanup_entries;
    1084             : 
    1085             :         /* There's a very annoying laxness in the dma_fence API here, in
    1086             :          * that backends are not required to automatically report when a
    1087             :          * fence is signaled prior to fence->ops->enable_signaling() being
    1088             :          * called.  So here if we fail to match signaled_count, we need to
    1089             :          * fallthough and try a 0 timeout wait!
    1090             :          */
    1091             : 
    1092           0 :         if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) {
    1093           0 :                 for (i = 0; i < count; ++i)
    1094           0 :                         drm_syncobj_fence_add_wait(syncobjs[i], &entries[i]);
    1095             :         }
    1096             : 
    1097             :         do {
    1098           0 :                 set_current_state(TASK_INTERRUPTIBLE);
    1099             : 
    1100           0 :                 signaled_count = 0;
    1101           0 :                 for (i = 0; i < count; ++i) {
    1102           0 :                         fence = entries[i].fence;
    1103           0 :                         if (!fence)
    1104           0 :                                 continue;
    1105             : 
    1106           0 :                         if ((flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE) ||
    1107           0 :                             dma_fence_is_signaled(fence) ||
    1108           0 :                             (!entries[i].fence_cb.func &&
    1109           0 :                              dma_fence_add_callback(fence,
    1110             :                                                     &entries[i].fence_cb,
    1111             :                                                     syncobj_wait_fence_func))) {
    1112             :                                 /* The fence has been signaled */
    1113           0 :                                 if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL) {
    1114           0 :                                         signaled_count++;
    1115             :                                 } else {
    1116           0 :                                         if (idx)
    1117           0 :                                                 *idx = i;
    1118             :                                         goto done_waiting;
    1119             :                                 }
    1120             :                         }
    1121             :                 }
    1122             : 
    1123           0 :                 if (signaled_count == count)
    1124             :                         goto done_waiting;
    1125             : 
    1126           0 :                 if (timeout == 0) {
    1127             :                         timeout = -ETIME;
    1128             :                         goto done_waiting;
    1129             :                 }
    1130             : 
    1131           0 :                 if (signal_pending(current)) {
    1132             :                         timeout = -ERESTARTSYS;
    1133             :                         goto done_waiting;
    1134             :                 }
    1135             : 
    1136           0 :                 timeout = schedule_timeout(timeout);
    1137             :         } while (1);
    1138             : 
    1139             : done_waiting:
    1140           0 :         __set_current_state(TASK_RUNNING);
    1141             : 
    1142             : cleanup_entries:
    1143           0 :         for (i = 0; i < count; ++i) {
    1144           0 :                 drm_syncobj_remove_wait(syncobjs[i], &entries[i]);
    1145           0 :                 if (entries[i].fence_cb.func)
    1146           0 :                         dma_fence_remove_callback(entries[i].fence,
    1147             :                                                   &entries[i].fence_cb);
    1148           0 :                 dma_fence_put(entries[i].fence);
    1149             :         }
    1150           0 :         kfree(entries);
    1151             : 
    1152             : err_free_points:
    1153           0 :         kfree(points);
    1154             : 
    1155           0 :         return timeout;
    1156             : }
    1157             : 
    1158             : /**
    1159             :  * drm_timeout_abs_to_jiffies - calculate jiffies timeout from absolute value
    1160             :  *
    1161             :  * @timeout_nsec: timeout nsec component in ns, 0 for poll
    1162             :  *
    1163             :  * Calculate the timeout in jiffies from an absolute time in sec/nsec.
    1164             :  */
    1165           0 : signed long drm_timeout_abs_to_jiffies(int64_t timeout_nsec)
    1166             : {
    1167             :         ktime_t abs_timeout, now;
    1168             :         u64 timeout_ns, timeout_jiffies64;
    1169             : 
    1170             :         /* make 0 timeout means poll - absolute 0 doesn't seem valid */
    1171           0 :         if (timeout_nsec == 0)
    1172             :                 return 0;
    1173             : 
    1174           0 :         abs_timeout = ns_to_ktime(timeout_nsec);
    1175           0 :         now = ktime_get();
    1176             : 
    1177           0 :         if (!ktime_after(abs_timeout, now))
    1178             :                 return 0;
    1179             : 
    1180           0 :         timeout_ns = ktime_to_ns(ktime_sub(abs_timeout, now));
    1181             : 
    1182           0 :         timeout_jiffies64 = nsecs_to_jiffies64(timeout_ns);
    1183             :         /*  clamp timeout to avoid infinite timeout */
    1184           0 :         if (timeout_jiffies64 >= MAX_SCHEDULE_TIMEOUT - 1)
    1185             :                 return MAX_SCHEDULE_TIMEOUT - 1;
    1186             : 
    1187           0 :         return timeout_jiffies64 + 1;
    1188             : }
    1189             : EXPORT_SYMBOL(drm_timeout_abs_to_jiffies);
    1190             : 
    1191           0 : static int drm_syncobj_array_wait(struct drm_device *dev,
    1192             :                                   struct drm_file *file_private,
    1193             :                                   struct drm_syncobj_wait *wait,
    1194             :                                   struct drm_syncobj_timeline_wait *timeline_wait,
    1195             :                                   struct drm_syncobj **syncobjs, bool timeline)
    1196             : {
    1197           0 :         signed long timeout = 0;
    1198           0 :         uint32_t first = ~0;
    1199             : 
    1200           0 :         if (!timeline) {
    1201           0 :                 timeout = drm_timeout_abs_to_jiffies(wait->timeout_nsec);
    1202           0 :                 timeout = drm_syncobj_array_wait_timeout(syncobjs,
    1203             :                                                          NULL,
    1204             :                                                          wait->count_handles,
    1205             :                                                          wait->flags,
    1206             :                                                          timeout, &first);
    1207           0 :                 if (timeout < 0)
    1208           0 :                         return timeout;
    1209           0 :                 wait->first_signaled = first;
    1210             :         } else {
    1211           0 :                 timeout = drm_timeout_abs_to_jiffies(timeline_wait->timeout_nsec);
    1212           0 :                 timeout = drm_syncobj_array_wait_timeout(syncobjs,
    1213           0 :                                                          u64_to_user_ptr(timeline_wait->points),
    1214             :                                                          timeline_wait->count_handles,
    1215             :                                                          timeline_wait->flags,
    1216             :                                                          timeout, &first);
    1217           0 :                 if (timeout < 0)
    1218           0 :                         return timeout;
    1219           0 :                 timeline_wait->first_signaled = first;
    1220             :         }
    1221             :         return 0;
    1222             : }
    1223             : 
    1224           0 : static int drm_syncobj_array_find(struct drm_file *file_private,
    1225             :                                   void __user *user_handles,
    1226             :                                   uint32_t count_handles,
    1227             :                                   struct drm_syncobj ***syncobjs_out)
    1228             : {
    1229             :         uint32_t i, *handles;
    1230             :         struct drm_syncobj **syncobjs;
    1231             :         int ret;
    1232             : 
    1233           0 :         handles = kmalloc_array(count_handles, sizeof(*handles), GFP_KERNEL);
    1234           0 :         if (handles == NULL)
    1235             :                 return -ENOMEM;
    1236             : 
    1237           0 :         if (copy_from_user(handles, user_handles,
    1238             :                            sizeof(uint32_t) * count_handles)) {
    1239             :                 ret = -EFAULT;
    1240             :                 goto err_free_handles;
    1241             :         }
    1242             : 
    1243           0 :         syncobjs = kmalloc_array(count_handles, sizeof(*syncobjs), GFP_KERNEL);
    1244           0 :         if (syncobjs == NULL) {
    1245             :                 ret = -ENOMEM;
    1246             :                 goto err_free_handles;
    1247             :         }
    1248             : 
    1249           0 :         for (i = 0; i < count_handles; i++) {
    1250           0 :                 syncobjs[i] = drm_syncobj_find(file_private, handles[i]);
    1251           0 :                 if (!syncobjs[i]) {
    1252             :                         ret = -ENOENT;
    1253             :                         goto err_put_syncobjs;
    1254             :                 }
    1255             :         }
    1256             : 
    1257           0 :         kfree(handles);
    1258           0 :         *syncobjs_out = syncobjs;
    1259           0 :         return 0;
    1260             : 
    1261             : err_put_syncobjs:
    1262           0 :         while (i-- > 0)
    1263           0 :                 drm_syncobj_put(syncobjs[i]);
    1264           0 :         kfree(syncobjs);
    1265             : err_free_handles:
    1266           0 :         kfree(handles);
    1267             : 
    1268           0 :         return ret;
    1269             : }
    1270             : 
    1271           0 : static void drm_syncobj_array_free(struct drm_syncobj **syncobjs,
    1272             :                                    uint32_t count)
    1273             : {
    1274             :         uint32_t i;
    1275             : 
    1276           0 :         for (i = 0; i < count; i++)
    1277           0 :                 drm_syncobj_put(syncobjs[i]);
    1278           0 :         kfree(syncobjs);
    1279           0 : }
    1280             : 
    1281             : int
    1282           0 : drm_syncobj_wait_ioctl(struct drm_device *dev, void *data,
    1283             :                        struct drm_file *file_private)
    1284             : {
    1285           0 :         struct drm_syncobj_wait *args = data;
    1286             :         struct drm_syncobj **syncobjs;
    1287           0 :         int ret = 0;
    1288             : 
    1289           0 :         if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
    1290             :                 return -EOPNOTSUPP;
    1291             : 
    1292           0 :         if (args->flags & ~(DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL |
    1293             :                             DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT))
    1294             :                 return -EINVAL;
    1295             : 
    1296           0 :         if (args->count_handles == 0)
    1297             :                 return -EINVAL;
    1298             : 
    1299           0 :         ret = drm_syncobj_array_find(file_private,
    1300           0 :                                      u64_to_user_ptr(args->handles),
    1301             :                                      args->count_handles,
    1302             :                                      &syncobjs);
    1303           0 :         if (ret < 0)
    1304             :                 return ret;
    1305             : 
    1306           0 :         ret = drm_syncobj_array_wait(dev, file_private,
    1307             :                                      args, NULL, syncobjs, false);
    1308             : 
    1309           0 :         drm_syncobj_array_free(syncobjs, args->count_handles);
    1310             : 
    1311           0 :         return ret;
    1312             : }
    1313             : 
    1314             : int
    1315           0 : drm_syncobj_timeline_wait_ioctl(struct drm_device *dev, void *data,
    1316             :                                 struct drm_file *file_private)
    1317             : {
    1318           0 :         struct drm_syncobj_timeline_wait *args = data;
    1319             :         struct drm_syncobj **syncobjs;
    1320           0 :         int ret = 0;
    1321             : 
    1322           0 :         if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
    1323             :                 return -EOPNOTSUPP;
    1324             : 
    1325           0 :         if (args->flags & ~(DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL |
    1326             :                             DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
    1327             :                             DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE))
    1328             :                 return -EINVAL;
    1329             : 
    1330           0 :         if (args->count_handles == 0)
    1331             :                 return -EINVAL;
    1332             : 
    1333           0 :         ret = drm_syncobj_array_find(file_private,
    1334           0 :                                      u64_to_user_ptr(args->handles),
    1335             :                                      args->count_handles,
    1336             :                                      &syncobjs);
    1337           0 :         if (ret < 0)
    1338             :                 return ret;
    1339             : 
    1340           0 :         ret = drm_syncobj_array_wait(dev, file_private,
    1341             :                                      NULL, args, syncobjs, true);
    1342             : 
    1343           0 :         drm_syncobj_array_free(syncobjs, args->count_handles);
    1344             : 
    1345           0 :         return ret;
    1346             : }
    1347             : 
    1348             : 
    1349             : int
    1350           0 : drm_syncobj_reset_ioctl(struct drm_device *dev, void *data,
    1351             :                         struct drm_file *file_private)
    1352             : {
    1353           0 :         struct drm_syncobj_array *args = data;
    1354             :         struct drm_syncobj **syncobjs;
    1355             :         uint32_t i;
    1356             :         int ret;
    1357             : 
    1358           0 :         if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
    1359             :                 return -EOPNOTSUPP;
    1360             : 
    1361           0 :         if (args->pad != 0)
    1362             :                 return -EINVAL;
    1363             : 
    1364           0 :         if (args->count_handles == 0)
    1365             :                 return -EINVAL;
    1366             : 
    1367           0 :         ret = drm_syncobj_array_find(file_private,
    1368           0 :                                      u64_to_user_ptr(args->handles),
    1369             :                                      args->count_handles,
    1370             :                                      &syncobjs);
    1371           0 :         if (ret < 0)
    1372             :                 return ret;
    1373             : 
    1374           0 :         for (i = 0; i < args->count_handles; i++)
    1375           0 :                 drm_syncobj_replace_fence(syncobjs[i], NULL);
    1376             : 
    1377           0 :         drm_syncobj_array_free(syncobjs, args->count_handles);
    1378             : 
    1379           0 :         return 0;
    1380             : }
    1381             : 
    1382             : int
    1383           0 : drm_syncobj_signal_ioctl(struct drm_device *dev, void *data,
    1384             :                          struct drm_file *file_private)
    1385             : {
    1386           0 :         struct drm_syncobj_array *args = data;
    1387             :         struct drm_syncobj **syncobjs;
    1388             :         uint32_t i;
    1389             :         int ret;
    1390             : 
    1391           0 :         if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
    1392             :                 return -EOPNOTSUPP;
    1393             : 
    1394           0 :         if (args->pad != 0)
    1395             :                 return -EINVAL;
    1396             : 
    1397           0 :         if (args->count_handles == 0)
    1398             :                 return -EINVAL;
    1399             : 
    1400           0 :         ret = drm_syncobj_array_find(file_private,
    1401           0 :                                      u64_to_user_ptr(args->handles),
    1402             :                                      args->count_handles,
    1403             :                                      &syncobjs);
    1404           0 :         if (ret < 0)
    1405             :                 return ret;
    1406             : 
    1407           0 :         for (i = 0; i < args->count_handles; i++) {
    1408           0 :                 ret = drm_syncobj_assign_null_handle(syncobjs[i]);
    1409           0 :                 if (ret < 0)
    1410             :                         break;
    1411             :         }
    1412             : 
    1413           0 :         drm_syncobj_array_free(syncobjs, args->count_handles);
    1414             : 
    1415           0 :         return ret;
    1416             : }
    1417             : 
    1418             : int
    1419           0 : drm_syncobj_timeline_signal_ioctl(struct drm_device *dev, void *data,
    1420             :                                   struct drm_file *file_private)
    1421             : {
    1422           0 :         struct drm_syncobj_timeline_array *args = data;
    1423             :         struct drm_syncobj **syncobjs;
    1424             :         struct dma_fence_chain **chains;
    1425             :         uint64_t *points;
    1426             :         uint32_t i, j;
    1427             :         int ret;
    1428             : 
    1429           0 :         if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
    1430             :                 return -EOPNOTSUPP;
    1431             : 
    1432           0 :         if (args->flags != 0)
    1433             :                 return -EINVAL;
    1434             : 
    1435           0 :         if (args->count_handles == 0)
    1436             :                 return -EINVAL;
    1437             : 
    1438           0 :         ret = drm_syncobj_array_find(file_private,
    1439           0 :                                      u64_to_user_ptr(args->handles),
    1440             :                                      args->count_handles,
    1441             :                                      &syncobjs);
    1442           0 :         if (ret < 0)
    1443             :                 return ret;
    1444             : 
    1445           0 :         points = kmalloc_array(args->count_handles, sizeof(*points),
    1446             :                                GFP_KERNEL);
    1447           0 :         if (!points) {
    1448             :                 ret = -ENOMEM;
    1449             :                 goto out;
    1450             :         }
    1451           0 :         if (!u64_to_user_ptr(args->points)) {
    1452           0 :                 memset(points, 0, args->count_handles * sizeof(uint64_t));
    1453           0 :         } else if (copy_from_user(points, u64_to_user_ptr(args->points),
    1454           0 :                                   sizeof(uint64_t) * args->count_handles)) {
    1455             :                 ret = -EFAULT;
    1456             :                 goto err_points;
    1457             :         }
    1458             : 
    1459           0 :         chains = kmalloc_array(args->count_handles, sizeof(void *), GFP_KERNEL);
    1460           0 :         if (!chains) {
    1461             :                 ret = -ENOMEM;
    1462             :                 goto err_points;
    1463             :         }
    1464           0 :         for (i = 0; i < args->count_handles; i++) {
    1465           0 :                 chains[i] = dma_fence_chain_alloc();
    1466           0 :                 if (!chains[i]) {
    1467           0 :                         for (j = 0; j < i; j++)
    1468           0 :                                 dma_fence_chain_free(chains[j]);
    1469             :                         ret = -ENOMEM;
    1470             :                         goto err_chains;
    1471             :                 }
    1472             :         }
    1473             : 
    1474           0 :         for (i = 0; i < args->count_handles; i++) {
    1475           0 :                 struct dma_fence *fence = dma_fence_get_stub();
    1476             : 
    1477           0 :                 drm_syncobj_add_point(syncobjs[i], chains[i],
    1478           0 :                                       fence, points[i]);
    1479           0 :                 dma_fence_put(fence);
    1480             :         }
    1481             : err_chains:
    1482           0 :         kfree(chains);
    1483             : err_points:
    1484           0 :         kfree(points);
    1485             : out:
    1486           0 :         drm_syncobj_array_free(syncobjs, args->count_handles);
    1487             : 
    1488           0 :         return ret;
    1489             : }
    1490             : 
    1491           0 : int drm_syncobj_query_ioctl(struct drm_device *dev, void *data,
    1492             :                             struct drm_file *file_private)
    1493             : {
    1494           0 :         struct drm_syncobj_timeline_array *args = data;
    1495             :         struct drm_syncobj **syncobjs;
    1496           0 :         uint64_t __user *points = u64_to_user_ptr(args->points);
    1497             :         uint32_t i;
    1498             :         int ret;
    1499             : 
    1500           0 :         if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
    1501             :                 return -EOPNOTSUPP;
    1502             : 
    1503           0 :         if (args->flags & ~DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED)
    1504             :                 return -EINVAL;
    1505             : 
    1506           0 :         if (args->count_handles == 0)
    1507             :                 return -EINVAL;
    1508             : 
    1509           0 :         ret = drm_syncobj_array_find(file_private,
    1510           0 :                                      u64_to_user_ptr(args->handles),
    1511             :                                      args->count_handles,
    1512             :                                      &syncobjs);
    1513           0 :         if (ret < 0)
    1514             :                 return ret;
    1515             : 
    1516           0 :         for (i = 0; i < args->count_handles; i++) {
    1517             :                 struct dma_fence_chain *chain;
    1518             :                 struct dma_fence *fence;
    1519             :                 uint64_t point;
    1520             : 
    1521           0 :                 fence = drm_syncobj_fence_get(syncobjs[i]);
    1522           0 :                 chain = to_dma_fence_chain(fence);
    1523           0 :                 if (chain) {
    1524           0 :                         struct dma_fence *iter, *last_signaled =
    1525             :                                 dma_fence_get(fence);
    1526             : 
    1527           0 :                         if (args->flags &
    1528             :                             DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED) {
    1529           0 :                                 point = fence->seqno;
    1530             :                         } else {
    1531           0 :                                 dma_fence_chain_for_each(iter, fence) {
    1532           0 :                                         if (iter->context != fence->context) {
    1533             :                                                 dma_fence_put(iter);
    1534             :                                                 /* It is most likely that timeline has
    1535             :                                                 * unorder points. */
    1536             :                                                 break;
    1537             :                                         }
    1538           0 :                                         dma_fence_put(last_signaled);
    1539           0 :                                         last_signaled = dma_fence_get(iter);
    1540             :                                 }
    1541           0 :                                 point = dma_fence_is_signaled(last_signaled) ?
    1542           0 :                                         last_signaled->seqno :
    1543           0 :                                         to_dma_fence_chain(last_signaled)->prev_seqno;
    1544             :                         }
    1545             :                         dma_fence_put(last_signaled);
    1546             :                 } else {
    1547           0 :                         point = 0;
    1548             :                 }
    1549           0 :                 dma_fence_put(fence);
    1550           0 :                 ret = copy_to_user(&points[i], &point, sizeof(uint64_t));
    1551           0 :                 ret = ret ? -EFAULT : 0;
    1552           0 :                 if (ret)
    1553             :                         break;
    1554             :         }
    1555           0 :         drm_syncobj_array_free(syncobjs, args->count_handles);
    1556             : 
    1557           0 :         return ret;
    1558             : }

Generated by: LCOV version 1.14