1*e88c3a9bSriastradh /* $NetBSD: drm_syncobj.c,v 1.7 2021/12/19 12:35:45 riastradh Exp $ */
24e390cabSriastradh
34e390cabSriastradh /*
44e390cabSriastradh * Copyright 2017 Red Hat
54e390cabSriastradh * Parts ported from amdgpu (fence wait code).
64e390cabSriastradh * Copyright 2016 Advanced Micro Devices, Inc.
74e390cabSriastradh *
84e390cabSriastradh * Permission is hereby granted, free of charge, to any person obtaining a
94e390cabSriastradh * copy of this software and associated documentation files (the "Software"),
104e390cabSriastradh * to deal in the Software without restriction, including without limitation
114e390cabSriastradh * the rights to use, copy, modify, merge, publish, distribute, sublicense,
124e390cabSriastradh * and/or sell copies of the Software, and to permit persons to whom the
134e390cabSriastradh * Software is furnished to do so, subject to the following conditions:
144e390cabSriastradh *
154e390cabSriastradh * The above copyright notice and this permission notice (including the next
164e390cabSriastradh * paragraph) shall be included in all copies or substantial portions of the
174e390cabSriastradh * Software.
184e390cabSriastradh *
194e390cabSriastradh * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
204e390cabSriastradh * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
214e390cabSriastradh * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
224e390cabSriastradh * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
234e390cabSriastradh * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
244e390cabSriastradh * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
254e390cabSriastradh * IN THE SOFTWARE.
264e390cabSriastradh *
274e390cabSriastradh * Authors:
284e390cabSriastradh *
294e390cabSriastradh */
304e390cabSriastradh
314e390cabSriastradh /**
324e390cabSriastradh * DOC: Overview
334e390cabSriastradh *
344e390cabSriastradh * DRM synchronisation objects (syncobj, see struct &drm_syncobj) provide a
354e390cabSriastradh * container for a synchronization primitive which can be used by userspace
364e390cabSriastradh * to explicitly synchronize GPU commands, can be shared between userspace
374e390cabSriastradh * processes, and can be shared between different DRM drivers.
384e390cabSriastradh * Their primary use-case is to implement Vulkan fences and semaphores.
394e390cabSriastradh * The syncobj userspace API provides ioctls for several operations:
404e390cabSriastradh *
414e390cabSriastradh * - Creation and destruction of syncobjs
424e390cabSriastradh * - Import and export of syncobjs to/from a syncobj file descriptor
434e390cabSriastradh * - Import and export a syncobj's underlying fence to/from a sync file
444e390cabSriastradh * - Reset a syncobj (set its fence to NULL)
454e390cabSriastradh * - Signal a syncobj (set a trivially signaled fence)
464e390cabSriastradh * - Wait for a syncobj's fence to appear and be signaled
474e390cabSriastradh *
484e390cabSriastradh * At it's core, a syncobj is simply a wrapper around a pointer to a struct
494e390cabSriastradh * &dma_fence which may be NULL.
504e390cabSriastradh * When a syncobj is first created, its pointer is either NULL or a pointer
514e390cabSriastradh * to an already signaled fence depending on whether the
524e390cabSriastradh * &DRM_SYNCOBJ_CREATE_SIGNALED flag is passed to
534e390cabSriastradh * &DRM_IOCTL_SYNCOBJ_CREATE.
544e390cabSriastradh * When GPU work which signals a syncobj is enqueued in a DRM driver,
554e390cabSriastradh * the syncobj fence is replaced with a fence which will be signaled by the
564e390cabSriastradh * completion of that work.
574e390cabSriastradh * When GPU work which waits on a syncobj is enqueued in a DRM driver, the
584e390cabSriastradh * driver retrieves syncobj's current fence at the time the work is enqueued
594e390cabSriastradh * waits on that fence before submitting the work to hardware.
604e390cabSriastradh * If the syncobj's fence is NULL, the enqueue operation is expected to fail.
614e390cabSriastradh * All manipulation of the syncobjs's fence happens in terms of the current
624e390cabSriastradh * fence at the time the ioctl is called by userspace regardless of whether
634e390cabSriastradh * that operation is an immediate host-side operation (signal or reset) or
644e390cabSriastradh * or an operation which is enqueued in some driver queue.
654e390cabSriastradh * &DRM_IOCTL_SYNCOBJ_RESET and &DRM_IOCTL_SYNCOBJ_SIGNAL can be used to
664e390cabSriastradh * manipulate a syncobj from the host by resetting its pointer to NULL or
674e390cabSriastradh * setting its pointer to a fence which is already signaled.
684e390cabSriastradh *
694e390cabSriastradh *
704e390cabSriastradh * Host-side wait on syncobjs
714e390cabSriastradh * --------------------------
724e390cabSriastradh *
734e390cabSriastradh * &DRM_IOCTL_SYNCOBJ_WAIT takes an array of syncobj handles and does a
744e390cabSriastradh * host-side wait on all of the syncobj fences simultaneously.
754e390cabSriastradh * If &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL is set, the wait ioctl will wait on
764e390cabSriastradh * all of the syncobj fences to be signaled before it returns.
774e390cabSriastradh * Otherwise, it returns once at least one syncobj fence has been signaled
784e390cabSriastradh * and the index of a signaled fence is written back to the client.
794e390cabSriastradh *
804e390cabSriastradh * Unlike the enqueued GPU work dependencies which fail if they see a NULL
814e390cabSriastradh * fence in a syncobj, if &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT is set,
824e390cabSriastradh * the host-side wait will first wait for the syncobj to receive a non-NULL
834e390cabSriastradh * fence and then wait on that fence.
844e390cabSriastradh * If &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT is not set and any one of the
854e390cabSriastradh * syncobjs in the array has a NULL fence, -EINVAL will be returned.
864e390cabSriastradh * Assuming the syncobj starts off with a NULL fence, this allows a client
874e390cabSriastradh * to do a host wait in one thread (or process) which waits on GPU work
884e390cabSriastradh * submitted in another thread (or process) without having to manually
894e390cabSriastradh * synchronize between the two.
904e390cabSriastradh * This requirement is inherited from the Vulkan fence API.
914e390cabSriastradh *
924e390cabSriastradh *
934e390cabSriastradh * Import/export of syncobjs
944e390cabSriastradh * -------------------------
954e390cabSriastradh *
964e390cabSriastradh * &DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE and &DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD
974e390cabSriastradh * provide two mechanisms for import/export of syncobjs.
984e390cabSriastradh *
994e390cabSriastradh * The first lets the client import or export an entire syncobj to a file
1004e390cabSriastradh * descriptor.
1014e390cabSriastradh * These fd's are opaque and have no other use case, except passing the
1024e390cabSriastradh * syncobj between processes.
1034e390cabSriastradh * All exported file descriptors and any syncobj handles created as a
1044e390cabSriastradh * result of importing those file descriptors own a reference to the
1054e390cabSriastradh * same underlying struct &drm_syncobj and the syncobj can be used
1064e390cabSriastradh * persistently across all the processes with which it is shared.
1074e390cabSriastradh * The syncobj is freed only once the last reference is dropped.
1084e390cabSriastradh * Unlike dma-buf, importing a syncobj creates a new handle (with its own
1094e390cabSriastradh * reference) for every import instead of de-duplicating.
1104e390cabSriastradh * The primary use-case of this persistent import/export is for shared
1114e390cabSriastradh * Vulkan fences and semaphores.
1124e390cabSriastradh *
1134e390cabSriastradh * The second import/export mechanism, which is indicated by
1144e390cabSriastradh * &DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE or
1154e390cabSriastradh * &DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE lets the client
1164e390cabSriastradh * import/export the syncobj's current fence from/to a &sync_file.
1174e390cabSriastradh * When a syncobj is exported to a sync file, that sync file wraps the
1184e390cabSriastradh * sycnobj's fence at the time of export and any later signal or reset
1194e390cabSriastradh * operations on the syncobj will not affect the exported sync file.
1204e390cabSriastradh * When a sync file is imported into a syncobj, the syncobj's fence is set
1214e390cabSriastradh * to the fence wrapped by that sync file.
1224e390cabSriastradh * Because sync files are immutable, resetting or signaling the syncobj
1234e390cabSriastradh * will not affect any sync files whose fences have been imported into the
1244e390cabSriastradh * syncobj.
1254e390cabSriastradh */
1264e390cabSriastradh
1274e390cabSriastradh #include <sys/cdefs.h>
128*e88c3a9bSriastradh __KERNEL_RCSID(0, "$NetBSD: drm_syncobj.c,v 1.7 2021/12/19 12:35:45 riastradh Exp $");
1294e390cabSriastradh
1304e390cabSriastradh #include <linux/anon_inodes.h>
1314e390cabSriastradh #include <linux/file.h>
1324e390cabSriastradh #include <linux/fs.h>
1334e390cabSriastradh #include <linux/sched/signal.h>
1344e390cabSriastradh #include <linux/sync_file.h>
1354e390cabSriastradh #include <linux/uaccess.h>
1364e390cabSriastradh
1374e390cabSriastradh #include <drm/drm.h>
1384e390cabSriastradh #include <drm/drm_drv.h>
1394e390cabSriastradh #include <drm/drm_file.h>
1404e390cabSriastradh #include <drm/drm_gem.h>
1414e390cabSriastradh #include <drm/drm_print.h>
1424e390cabSriastradh #include <drm/drm_syncobj.h>
1434e390cabSriastradh #include <drm/drm_utils.h>
1444e390cabSriastradh
1454e390cabSriastradh #include "drm_internal.h"
1464e390cabSriastradh
1474e390cabSriastradh struct syncobj_wait_entry {
1484e390cabSriastradh struct list_head node;
149679fafc5Sriastradh #ifdef __NetBSD__
15005319ba3Sriastradh /*
15105319ba3Sriastradh * Lock order:
15205319ba3Sriastradh * syncobj->lock ???? fence lock
15305319ba3Sriastradh * syncobj->lock then wait->lock
15405319ba3Sriastradh * fence lock then wait->lock
15505319ba3Sriastradh *
15605319ba3Sriastradh * syncobj->lock serializes wait->node and wait->fence.
15705319ba3Sriastradh * wait->lock serializes wait->signalledp, and, by
15805319ba3Sriastradh * interlocking with syncobj->lock, coordinates wakeups on
15905319ba3Sriastradh * wait->cv for wait->fence.
16005319ba3Sriastradh */
161679fafc5Sriastradh kmutex_t *lock;
162679fafc5Sriastradh kcondvar_t *cv;
16305319ba3Sriastradh bool *signalledp;
164679fafc5Sriastradh #else
1654e390cabSriastradh struct task_struct *task;
166679fafc5Sriastradh #endif
1674e390cabSriastradh struct dma_fence *fence;
1684e390cabSriastradh struct dma_fence_cb fence_cb;
1694e390cabSriastradh u64 point;
1704e390cabSriastradh };
1714e390cabSriastradh
1724e390cabSriastradh static void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj,
1734e390cabSriastradh struct syncobj_wait_entry *wait);
1744e390cabSriastradh
1754e390cabSriastradh /**
1764e390cabSriastradh * drm_syncobj_find - lookup and reference a sync object.
1774e390cabSriastradh * @file_private: drm file private pointer
1784e390cabSriastradh * @handle: sync object handle to lookup.
1794e390cabSriastradh *
1804e390cabSriastradh * Returns a reference to the syncobj pointed to by handle or NULL. The
1814e390cabSriastradh * reference must be released by calling drm_syncobj_put().
1824e390cabSriastradh */
drm_syncobj_find(struct drm_file * file_private,u32 handle)1834e390cabSriastradh struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private,
1844e390cabSriastradh u32 handle)
1854e390cabSriastradh {
1864e390cabSriastradh struct drm_syncobj *syncobj;
1874e390cabSriastradh
1884e390cabSriastradh spin_lock(&file_private->syncobj_table_lock);
1894e390cabSriastradh
1904e390cabSriastradh /* Check if we currently have a reference on the object */
1914e390cabSriastradh syncobj = idr_find(&file_private->syncobj_idr, handle);
1924e390cabSriastradh if (syncobj)
1934e390cabSriastradh drm_syncobj_get(syncobj);
1944e390cabSriastradh
1954e390cabSriastradh spin_unlock(&file_private->syncobj_table_lock);
1964e390cabSriastradh
1974e390cabSriastradh return syncobj;
1984e390cabSriastradh }
1994e390cabSriastradh EXPORT_SYMBOL(drm_syncobj_find);
2004e390cabSriastradh
drm_syncobj_fence_add_wait(struct drm_syncobj * syncobj,struct syncobj_wait_entry * wait)2014e390cabSriastradh static void drm_syncobj_fence_add_wait(struct drm_syncobj *syncobj,
2024e390cabSriastradh struct syncobj_wait_entry *wait)
2034e390cabSriastradh {
2044e390cabSriastradh struct dma_fence *fence;
2054e390cabSriastradh
2064e390cabSriastradh if (wait->fence)
2074e390cabSriastradh return;
2084e390cabSriastradh
2094e390cabSriastradh spin_lock(&syncobj->lock);
2104e390cabSriastradh /* We've already tried once to get a fence and failed. Now that we
2114e390cabSriastradh * have the lock, try one more time just to be sure we don't add a
2124e390cabSriastradh * callback when a fence has already been set.
2134e390cabSriastradh */
2144e390cabSriastradh fence = dma_fence_get(rcu_dereference_protected(syncobj->fence, 1));
2154e390cabSriastradh if (!fence || dma_fence_chain_find_seqno(&fence, wait->point)) {
2164e390cabSriastradh dma_fence_put(fence);
2174e390cabSriastradh list_add_tail(&wait->node, &syncobj->cb_list);
2184e390cabSriastradh } else if (!fence) {
2194e390cabSriastradh wait->fence = dma_fence_get_stub();
2204e390cabSriastradh } else {
2214e390cabSriastradh wait->fence = fence;
2224e390cabSriastradh }
2234e390cabSriastradh spin_unlock(&syncobj->lock);
2244e390cabSriastradh }
2254e390cabSriastradh
drm_syncobj_remove_wait(struct drm_syncobj * syncobj,struct syncobj_wait_entry * wait)2264e390cabSriastradh static void drm_syncobj_remove_wait(struct drm_syncobj *syncobj,
2274e390cabSriastradh struct syncobj_wait_entry *wait)
2284e390cabSriastradh {
2294e390cabSriastradh if (!wait->node.next)
2304e390cabSriastradh return;
2314e390cabSriastradh
2324e390cabSriastradh spin_lock(&syncobj->lock);
2334e390cabSriastradh list_del_init(&wait->node);
2344e390cabSriastradh spin_unlock(&syncobj->lock);
2354e390cabSriastradh }
2364e390cabSriastradh
2374e390cabSriastradh /**
2384e390cabSriastradh * drm_syncobj_add_point - add new timeline point to the syncobj
2394e390cabSriastradh * @syncobj: sync object to add timeline point do
2404e390cabSriastradh * @chain: chain node to use to add the point
2414e390cabSriastradh * @fence: fence to encapsulate in the chain node
2424e390cabSriastradh * @point: sequence number to use for the point
2434e390cabSriastradh *
2444e390cabSriastradh * Add the chain node as new timeline point to the syncobj.
2454e390cabSriastradh */
drm_syncobj_add_point(struct drm_syncobj * syncobj,struct dma_fence_chain * chain,struct dma_fence * fence,uint64_t point)2464e390cabSriastradh void drm_syncobj_add_point(struct drm_syncobj *syncobj,
2474e390cabSriastradh struct dma_fence_chain *chain,
2484e390cabSriastradh struct dma_fence *fence,
2494e390cabSriastradh uint64_t point)
2504e390cabSriastradh {
2514e390cabSriastradh struct syncobj_wait_entry *cur, *tmp;
2524e390cabSriastradh struct dma_fence *prev;
2534e390cabSriastradh
2544e390cabSriastradh dma_fence_get(fence);
2554e390cabSriastradh
2564e390cabSriastradh spin_lock(&syncobj->lock);
2574e390cabSriastradh
2584e390cabSriastradh prev = drm_syncobj_fence_get(syncobj);
2594e390cabSriastradh /* You are adding an unorder point to timeline, which could cause payload returned from query_ioctl is 0! */
2604e390cabSriastradh if (prev && prev->seqno >= point)
2614e390cabSriastradh DRM_ERROR("You are adding an unorder point to timeline!\n");
2624e390cabSriastradh dma_fence_chain_init(chain, prev, fence, point);
2634e390cabSriastradh rcu_assign_pointer(syncobj->fence, &chain->base);
2644e390cabSriastradh
2654e390cabSriastradh list_for_each_entry_safe(cur, tmp, &syncobj->cb_list, node)
2664e390cabSriastradh syncobj_wait_syncobj_func(syncobj, cur);
2674e390cabSriastradh spin_unlock(&syncobj->lock);
2684e390cabSriastradh
2694e390cabSriastradh /* Walk the chain once to trigger garbage collection */
2704e390cabSriastradh dma_fence_chain_for_each(fence, prev);
2714e390cabSriastradh dma_fence_put(prev);
2724e390cabSriastradh }
2734e390cabSriastradh EXPORT_SYMBOL(drm_syncobj_add_point);
2744e390cabSriastradh
2754e390cabSriastradh /**
2764e390cabSriastradh * drm_syncobj_replace_fence - replace fence in a sync object.
2774e390cabSriastradh * @syncobj: Sync object to replace fence in
2784e390cabSriastradh * @fence: fence to install in sync file.
2794e390cabSriastradh *
2804e390cabSriastradh * This replaces the fence on a sync object.
2814e390cabSriastradh */
drm_syncobj_replace_fence(struct drm_syncobj * syncobj,struct dma_fence * fence)2824e390cabSriastradh void drm_syncobj_replace_fence(struct drm_syncobj *syncobj,
2834e390cabSriastradh struct dma_fence *fence)
2844e390cabSriastradh {
2854e390cabSriastradh struct dma_fence *old_fence;
2864e390cabSriastradh struct syncobj_wait_entry *cur, *tmp;
2874e390cabSriastradh
2884e390cabSriastradh if (fence)
2894e390cabSriastradh dma_fence_get(fence);
2904e390cabSriastradh
2914e390cabSriastradh spin_lock(&syncobj->lock);
2924e390cabSriastradh
2934e390cabSriastradh old_fence = rcu_dereference_protected(syncobj->fence,
2944e390cabSriastradh lockdep_is_held(&syncobj->lock));
2954e390cabSriastradh rcu_assign_pointer(syncobj->fence, fence);
2964e390cabSriastradh
2974e390cabSriastradh if (fence != old_fence) {
2984e390cabSriastradh list_for_each_entry_safe(cur, tmp, &syncobj->cb_list, node)
2994e390cabSriastradh syncobj_wait_syncobj_func(syncobj, cur);
3004e390cabSriastradh }
3014e390cabSriastradh
3024e390cabSriastradh spin_unlock(&syncobj->lock);
3034e390cabSriastradh
3044e390cabSriastradh dma_fence_put(old_fence);
3054e390cabSriastradh }
3064e390cabSriastradh EXPORT_SYMBOL(drm_syncobj_replace_fence);
3074e390cabSriastradh
3084e390cabSriastradh /**
3094e390cabSriastradh * drm_syncobj_assign_null_handle - assign a stub fence to the sync object
3104e390cabSriastradh * @syncobj: sync object to assign the fence on
3114e390cabSriastradh *
3124e390cabSriastradh * Assign a already signaled stub fence to the sync object.
3134e390cabSriastradh */
drm_syncobj_assign_null_handle(struct drm_syncobj * syncobj)3144e390cabSriastradh static void drm_syncobj_assign_null_handle(struct drm_syncobj *syncobj)
3154e390cabSriastradh {
3164e390cabSriastradh struct dma_fence *fence = dma_fence_get_stub();
3174e390cabSriastradh
3184e390cabSriastradh drm_syncobj_replace_fence(syncobj, fence);
3194e390cabSriastradh dma_fence_put(fence);
3204e390cabSriastradh }
3214e390cabSriastradh
3224e390cabSriastradh /* 5s default for wait submission */
3234e390cabSriastradh #define DRM_SYNCOBJ_WAIT_FOR_SUBMIT_TIMEOUT 5000000000ULL
3244e390cabSriastradh /**
3254e390cabSriastradh * drm_syncobj_find_fence - lookup and reference the fence in a sync object
3264e390cabSriastradh * @file_private: drm file private pointer
3274e390cabSriastradh * @handle: sync object handle to lookup.
3284e390cabSriastradh * @point: timeline point
3294e390cabSriastradh * @flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or not
3304e390cabSriastradh * @fence: out parameter for the fence
3314e390cabSriastradh *
3324e390cabSriastradh * This is just a convenience function that combines drm_syncobj_find() and
3334e390cabSriastradh * drm_syncobj_fence_get().
3344e390cabSriastradh *
3354e390cabSriastradh * Returns 0 on success or a negative error value on failure. On success @fence
3364e390cabSriastradh * contains a reference to the fence, which must be released by calling
3374e390cabSriastradh * dma_fence_put().
3384e390cabSriastradh */
drm_syncobj_find_fence(struct drm_file * file_private,u32 handle,u64 point,u64 flags,struct dma_fence ** fence)3394e390cabSriastradh int drm_syncobj_find_fence(struct drm_file *file_private,
3404e390cabSriastradh u32 handle, u64 point, u64 flags,
3414e390cabSriastradh struct dma_fence **fence)
3424e390cabSriastradh {
3434e390cabSriastradh struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);
3444e390cabSriastradh struct syncobj_wait_entry wait;
3454e390cabSriastradh u64 timeout = nsecs_to_jiffies64(DRM_SYNCOBJ_WAIT_FOR_SUBMIT_TIMEOUT);
3464e390cabSriastradh int ret;
3474e390cabSriastradh
3484e390cabSriastradh if (!syncobj)
3494e390cabSriastradh return -ENOENT;
3504e390cabSriastradh
3514e390cabSriastradh *fence = drm_syncobj_fence_get(syncobj);
3524e390cabSriastradh drm_syncobj_put(syncobj);
3534e390cabSriastradh
3544e390cabSriastradh if (*fence) {
3554e390cabSriastradh ret = dma_fence_chain_find_seqno(fence, point);
3564e390cabSriastradh if (!ret)
3574e390cabSriastradh return 0;
3584e390cabSriastradh dma_fence_put(*fence);
3594e390cabSriastradh } else {
3604e390cabSriastradh ret = -EINVAL;
3614e390cabSriastradh }
3624e390cabSriastradh
3634e390cabSriastradh if (!(flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT))
3644e390cabSriastradh return ret;
3654e390cabSriastradh
3664e390cabSriastradh memset(&wait, 0, sizeof(wait));
36705319ba3Sriastradh #ifdef __NetBSD__
36805319ba3Sriastradh kmutex_t lock;
36905319ba3Sriastradh kcondvar_t cv;
37005319ba3Sriastradh mutex_init(&lock, MUTEX_DEFAULT, IPL_VM);
37105319ba3Sriastradh cv_init(&cv, "drmfnfnc");
37205319ba3Sriastradh wait.cv = &cv;
37305319ba3Sriastradh #else
3744e390cabSriastradh wait.task = current;
37505319ba3Sriastradh #endif
376855555b4Sriastradh wait.point = point;
3774e390cabSriastradh drm_syncobj_fence_add_wait(syncobj, &wait);
3784e390cabSriastradh
37905319ba3Sriastradh #ifdef __NetBSD__
38005319ba3Sriastradh spin_lock(&syncobj->lock);
381*e88c3a9bSriastradh ret = 0;
382*e88c3a9bSriastradh while (wait.fence == NULL) {
383*e88c3a9bSriastradh unsigned start, end;
384*e88c3a9bSriastradh
385*e88c3a9bSriastradh if (timeout == 0) {
386*e88c3a9bSriastradh ret = -ETIME;
387*e88c3a9bSriastradh break;
388*e88c3a9bSriastradh }
389*e88c3a9bSriastradh mutex_spin_enter(&lock);
390*e88c3a9bSriastradh spin_unlock(&syncobj->lock);
391*e88c3a9bSriastradh start = getticks();
392*e88c3a9bSriastradh /* XXX errno NetBSD->Linux */
393*e88c3a9bSriastradh ret = -cv_timedwait_sig(&cv, &lock, MIN(timeout, INT_MAX/2));
394*e88c3a9bSriastradh end = getticks();
395*e88c3a9bSriastradh timeout -= MIN(timeout, end - start);
396*e88c3a9bSriastradh mutex_spin_exit(&lock);
397*e88c3a9bSriastradh spin_lock(&syncobj->lock);
398*e88c3a9bSriastradh KASSERTMSG((ret == 0 || ret == -EINTR || ret == -ERESTART ||
399*e88c3a9bSriastradh ret == -EWOULDBLOCK), "ret=%d", ret);
400*e88c3a9bSriastradh if (ret == -EINTR || ret == -ERESTART) {
401*e88c3a9bSriastradh ret = -ERESTARTSYS;
402*e88c3a9bSriastradh break;
403*e88c3a9bSriastradh } else if (ret == -EWOULDBLOCK) {
404*e88c3a9bSriastradh /* Check once more, then give up. */
405*e88c3a9bSriastradh ret = 0;
406*e88c3a9bSriastradh timeout = 0;
407*e88c3a9bSriastradh } else {
408*e88c3a9bSriastradh KASSERT(ret == 0);
409*e88c3a9bSriastradh }
410*e88c3a9bSriastradh }
411*e88c3a9bSriastradh *fence = wait.fence;
412*e88c3a9bSriastradh if (wait.node.next)
413*e88c3a9bSriastradh list_del_init(&wait.node);
414*e88c3a9bSriastradh spin_unlock(&syncobj->lock);
415*e88c3a9bSriastradh cv_destroy(&cv);
416*e88c3a9bSriastradh mutex_destroy(&lock);
417*e88c3a9bSriastradh #else
4184e390cabSriastradh do {
4194e390cabSriastradh set_current_state(TASK_INTERRUPTIBLE);
4204e390cabSriastradh if (wait.fence) {
4214e390cabSriastradh ret = 0;
4224e390cabSriastradh break;
4234e390cabSriastradh }
4244e390cabSriastradh if (timeout == 0) {
4254e390cabSriastradh ret = -ETIME;
4264e390cabSriastradh break;
4274e390cabSriastradh }
4284e390cabSriastradh
4294e390cabSriastradh if (signal_pending(current)) {
4304e390cabSriastradh ret = -ERESTARTSYS;
4314e390cabSriastradh break;
4324e390cabSriastradh }
4334e390cabSriastradh
4344e390cabSriastradh timeout = schedule_timeout(timeout);
4354e390cabSriastradh } while (1);
4364e390cabSriastradh
4374e390cabSriastradh __set_current_state(TASK_RUNNING);
4384e390cabSriastradh *fence = wait.fence;
4394e390cabSriastradh
4404e390cabSriastradh if (wait.node.next)
4414e390cabSriastradh drm_syncobj_remove_wait(syncobj, &wait);
442*e88c3a9bSriastradh #endif
4434e390cabSriastradh
4444e390cabSriastradh return ret;
4454e390cabSriastradh }
4464e390cabSriastradh EXPORT_SYMBOL(drm_syncobj_find_fence);
4474e390cabSriastradh
4484e390cabSriastradh /**
4494e390cabSriastradh * drm_syncobj_free - free a sync object.
4504e390cabSriastradh * @kref: kref to free.
4514e390cabSriastradh *
4524e390cabSriastradh * Only to be called from kref_put in drm_syncobj_put.
4534e390cabSriastradh */
drm_syncobj_free(struct kref * kref)4544e390cabSriastradh void drm_syncobj_free(struct kref *kref)
4554e390cabSriastradh {
4564e390cabSriastradh struct drm_syncobj *syncobj = container_of(kref,
4574e390cabSriastradh struct drm_syncobj,
4584e390cabSriastradh refcount);
4594e390cabSriastradh drm_syncobj_replace_fence(syncobj, NULL);
460816569f2Sriastradh spin_lock_destroy(&syncobj->lock);
4614e390cabSriastradh kfree(syncobj);
4624e390cabSriastradh }
4634e390cabSriastradh EXPORT_SYMBOL(drm_syncobj_free);
4644e390cabSriastradh
4654e390cabSriastradh /**
4664e390cabSriastradh * drm_syncobj_create - create a new syncobj
4674e390cabSriastradh * @out_syncobj: returned syncobj
4684e390cabSriastradh * @flags: DRM_SYNCOBJ_* flags
4694e390cabSriastradh * @fence: if non-NULL, the syncobj will represent this fence
4704e390cabSriastradh *
4714e390cabSriastradh * This is the first function to create a sync object. After creating, drivers
4724e390cabSriastradh * probably want to make it available to userspace, either through
4734e390cabSriastradh * drm_syncobj_get_handle() or drm_syncobj_get_fd().
4744e390cabSriastradh *
4754e390cabSriastradh * Returns 0 on success or a negative error value on failure.
4764e390cabSriastradh */
drm_syncobj_create(struct drm_syncobj ** out_syncobj,uint32_t flags,struct dma_fence * fence)4774e390cabSriastradh int drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags,
4784e390cabSriastradh struct dma_fence *fence)
4794e390cabSriastradh {
4804e390cabSriastradh struct drm_syncobj *syncobj;
4814e390cabSriastradh
4824e390cabSriastradh syncobj = kzalloc(sizeof(struct drm_syncobj), GFP_KERNEL);
4834e390cabSriastradh if (!syncobj)
4844e390cabSriastradh return -ENOMEM;
4854e390cabSriastradh
4864e390cabSriastradh kref_init(&syncobj->refcount);
4874e390cabSriastradh INIT_LIST_HEAD(&syncobj->cb_list);
4884e390cabSriastradh spin_lock_init(&syncobj->lock);
4894e390cabSriastradh
4904e390cabSriastradh if (flags & DRM_SYNCOBJ_CREATE_SIGNALED)
4914e390cabSriastradh drm_syncobj_assign_null_handle(syncobj);
4924e390cabSriastradh
4934e390cabSriastradh if (fence)
4944e390cabSriastradh drm_syncobj_replace_fence(syncobj, fence);
4954e390cabSriastradh
4964e390cabSriastradh *out_syncobj = syncobj;
4974e390cabSriastradh return 0;
4984e390cabSriastradh }
4994e390cabSriastradh EXPORT_SYMBOL(drm_syncobj_create);
5004e390cabSriastradh
5014e390cabSriastradh /**
5024e390cabSriastradh * drm_syncobj_get_handle - get a handle from a syncobj
5034e390cabSriastradh * @file_private: drm file private pointer
5044e390cabSriastradh * @syncobj: Sync object to export
5054e390cabSriastradh * @handle: out parameter with the new handle
5064e390cabSriastradh *
5074e390cabSriastradh * Exports a sync object created with drm_syncobj_create() as a handle on
5084e390cabSriastradh * @file_private to userspace.
5094e390cabSriastradh *
5104e390cabSriastradh * Returns 0 on success or a negative error value on failure.
5114e390cabSriastradh */
drm_syncobj_get_handle(struct drm_file * file_private,struct drm_syncobj * syncobj,u32 * handle)5124e390cabSriastradh int drm_syncobj_get_handle(struct drm_file *file_private,
5134e390cabSriastradh struct drm_syncobj *syncobj, u32 *handle)
5144e390cabSriastradh {
5154e390cabSriastradh int ret;
5164e390cabSriastradh
5174e390cabSriastradh /* take a reference to put in the idr */
5184e390cabSriastradh drm_syncobj_get(syncobj);
5194e390cabSriastradh
5204e390cabSriastradh idr_preload(GFP_KERNEL);
5214e390cabSriastradh spin_lock(&file_private->syncobj_table_lock);
5224e390cabSriastradh ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT);
5234e390cabSriastradh spin_unlock(&file_private->syncobj_table_lock);
5244e390cabSriastradh
5254e390cabSriastradh idr_preload_end();
5264e390cabSriastradh
5274e390cabSriastradh if (ret < 0) {
5284e390cabSriastradh drm_syncobj_put(syncobj);
5294e390cabSriastradh return ret;
5304e390cabSriastradh }
5314e390cabSriastradh
5324e390cabSriastradh *handle = ret;
5334e390cabSriastradh return 0;
5344e390cabSriastradh }
5354e390cabSriastradh EXPORT_SYMBOL(drm_syncobj_get_handle);
5364e390cabSriastradh
drm_syncobj_create_as_handle(struct drm_file * file_private,u32 * handle,uint32_t flags)5374e390cabSriastradh static int drm_syncobj_create_as_handle(struct drm_file *file_private,
5384e390cabSriastradh u32 *handle, uint32_t flags)
5394e390cabSriastradh {
5404e390cabSriastradh int ret;
5414e390cabSriastradh struct drm_syncobj *syncobj;
5424e390cabSriastradh
5434e390cabSriastradh ret = drm_syncobj_create(&syncobj, flags, NULL);
5444e390cabSriastradh if (ret)
5454e390cabSriastradh return ret;
5464e390cabSriastradh
5474e390cabSriastradh ret = drm_syncobj_get_handle(file_private, syncobj, handle);
5484e390cabSriastradh drm_syncobj_put(syncobj);
5494e390cabSriastradh return ret;
5504e390cabSriastradh }
5514e390cabSriastradh
drm_syncobj_destroy(struct drm_file * file_private,u32 handle)5524e390cabSriastradh static int drm_syncobj_destroy(struct drm_file *file_private,
5534e390cabSriastradh u32 handle)
5544e390cabSriastradh {
5554e390cabSriastradh struct drm_syncobj *syncobj;
5564e390cabSriastradh
5574e390cabSriastradh spin_lock(&file_private->syncobj_table_lock);
5584e390cabSriastradh syncobj = idr_remove(&file_private->syncobj_idr, handle);
5594e390cabSriastradh spin_unlock(&file_private->syncobj_table_lock);
5604e390cabSriastradh
5614e390cabSriastradh if (!syncobj)
5624e390cabSriastradh return -EINVAL;
5634e390cabSriastradh
5644e390cabSriastradh drm_syncobj_put(syncobj);
5654e390cabSriastradh return 0;
5664e390cabSriastradh }
5674e390cabSriastradh
568679fafc5Sriastradh #ifdef __NetBSD__
drm_syncobj_fop_close(struct file * file)569679fafc5Sriastradh static int drm_syncobj_fop_close(struct file *file)
570679fafc5Sriastradh #else
5714e390cabSriastradh static int drm_syncobj_file_release(struct inode *inode, struct file *file)
572679fafc5Sriastradh #endif
5734e390cabSriastradh {
574679fafc5Sriastradh #ifdef __NetBSD__
575679fafc5Sriastradh struct drm_syncobj *syncobj = file->f_data;
576679fafc5Sriastradh #else
5774e390cabSriastradh struct drm_syncobj *syncobj = file->private_data;
578679fafc5Sriastradh #endif
5794e390cabSriastradh
5804e390cabSriastradh drm_syncobj_put(syncobj);
5814e390cabSriastradh return 0;
5824e390cabSriastradh }
5834e390cabSriastradh
584679fafc5Sriastradh #ifdef __NetBSD__
585679fafc5Sriastradh static const struct fileops drm_syncobj_file_ops = {
586679fafc5Sriastradh .fo_name = "drm_syncobj",
587679fafc5Sriastradh .fo_read = fbadop_read,
588679fafc5Sriastradh .fo_write = fbadop_write,
589679fafc5Sriastradh .fo_ioctl = fbadop_ioctl,
590679fafc5Sriastradh .fo_fcntl = fnullop_fcntl,
591679fafc5Sriastradh .fo_poll = fnullop_poll,
592679fafc5Sriastradh .fo_stat = fbadop_stat,
593679fafc5Sriastradh .fo_close = drm_syncobj_fop_close,
594679fafc5Sriastradh .fo_kqfilter = fnullop_kqfilter,
595679fafc5Sriastradh .fo_restart = fnullop_restart,
596679fafc5Sriastradh };
597679fafc5Sriastradh #else
5984e390cabSriastradh static const struct file_operations drm_syncobj_file_fops = {
5994e390cabSriastradh .release = drm_syncobj_file_release,
6004e390cabSriastradh };
601679fafc5Sriastradh #endif
6024e390cabSriastradh
6034e390cabSriastradh /**
6044e390cabSriastradh * drm_syncobj_get_fd - get a file descriptor from a syncobj
6054e390cabSriastradh * @syncobj: Sync object to export
6064e390cabSriastradh * @p_fd: out parameter with the new file descriptor
6074e390cabSriastradh *
6084e390cabSriastradh * Exports a sync object created with drm_syncobj_create() as a file descriptor.
6094e390cabSriastradh *
6104e390cabSriastradh * Returns 0 on success or a negative error value on failure.
6114e390cabSriastradh */
drm_syncobj_get_fd(struct drm_syncobj * syncobj,int * p_fd)6124e390cabSriastradh int drm_syncobj_get_fd(struct drm_syncobj *syncobj, int *p_fd)
6134e390cabSriastradh {
6144e390cabSriastradh struct file *file;
6154e390cabSriastradh int fd;
616679fafc5Sriastradh #ifdef __NetBSD__
617679fafc5Sriastradh int ret;
618679fafc5Sriastradh #endif
6194e390cabSriastradh
620679fafc5Sriastradh #ifdef __NetBSD__
621679fafc5Sriastradh fd = -1;
622679fafc5Sriastradh /* XXX errno NetBSD->Linux */
623679fafc5Sriastradh ret = -fd_allocfile(&file, &fd);
624679fafc5Sriastradh if (ret)
625679fafc5Sriastradh return ret;
626679fafc5Sriastradh file->f_data = syncobj;
627679fafc5Sriastradh file->f_ops = &drm_syncobj_file_ops;
628679fafc5Sriastradh #else
6294e390cabSriastradh fd = get_unused_fd_flags(O_CLOEXEC);
6304e390cabSriastradh if (fd < 0)
6314e390cabSriastradh return fd;
6324e390cabSriastradh
6334e390cabSriastradh file = anon_inode_getfile("syncobj_file",
6344e390cabSriastradh &drm_syncobj_file_fops,
6354e390cabSriastradh syncobj, 0);
6364e390cabSriastradh if (IS_ERR(file)) {
6374e390cabSriastradh put_unused_fd(fd);
6384e390cabSriastradh return PTR_ERR(file);
6394e390cabSriastradh }
640679fafc5Sriastradh #endif
6414e390cabSriastradh
6424e390cabSriastradh drm_syncobj_get(syncobj);
6434e390cabSriastradh fd_install(fd, file);
6444e390cabSriastradh
6454e390cabSriastradh *p_fd = fd;
6464e390cabSriastradh return 0;
6474e390cabSriastradh }
6484e390cabSriastradh EXPORT_SYMBOL(drm_syncobj_get_fd);
6494e390cabSriastradh
drm_syncobj_handle_to_fd(struct drm_file * file_private,u32 handle,int * p_fd)6504e390cabSriastradh static int drm_syncobj_handle_to_fd(struct drm_file *file_private,
6514e390cabSriastradh u32 handle, int *p_fd)
6524e390cabSriastradh {
6534e390cabSriastradh struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);
6544e390cabSriastradh int ret;
6554e390cabSriastradh
6564e390cabSriastradh if (!syncobj)
6574e390cabSriastradh return -EINVAL;
6584e390cabSriastradh
6594e390cabSriastradh ret = drm_syncobj_get_fd(syncobj, p_fd);
6604e390cabSriastradh drm_syncobj_put(syncobj);
6614e390cabSriastradh return ret;
6624e390cabSriastradh }
6634e390cabSriastradh
drm_syncobj_fd_to_handle(struct drm_file * file_private,int fd,u32 * handle)6644e390cabSriastradh static int drm_syncobj_fd_to_handle(struct drm_file *file_private,
6654e390cabSriastradh int fd, u32 *handle)
6664e390cabSriastradh {
6674e390cabSriastradh struct drm_syncobj *syncobj;
6684e390cabSriastradh struct fd f = fdget(fd);
6694e390cabSriastradh int ret;
6704e390cabSriastradh
6714e390cabSriastradh if (!f.file)
6724e390cabSriastradh return -EINVAL;
6734e390cabSriastradh
674679fafc5Sriastradh #ifdef __NetBSD__
67505319ba3Sriastradh if (f.file->f_ops != &drm_syncobj_file_ops)
676679fafc5Sriastradh #else
67705319ba3Sriastradh if (f.file->f_op != &drm_syncobj_file_fops)
67805319ba3Sriastradh #endif
67905319ba3Sriastradh {
6804e390cabSriastradh fdput(f);
6814e390cabSriastradh return -EINVAL;
6824e390cabSriastradh }
6834e390cabSriastradh
6844e390cabSriastradh /* take a reference to put in the idr */
685679fafc5Sriastradh #ifdef __NetBSD__
686679fafc5Sriastradh syncobj = f.file->f_data;
687679fafc5Sriastradh #else
6884e390cabSriastradh syncobj = f.file->private_data;
689679fafc5Sriastradh #endif
6904e390cabSriastradh drm_syncobj_get(syncobj);
6914e390cabSriastradh
6924e390cabSriastradh idr_preload(GFP_KERNEL);
6934e390cabSriastradh spin_lock(&file_private->syncobj_table_lock);
6944e390cabSriastradh ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT);
6954e390cabSriastradh spin_unlock(&file_private->syncobj_table_lock);
6964e390cabSriastradh idr_preload_end();
6974e390cabSriastradh
6984e390cabSriastradh if (ret > 0) {
6994e390cabSriastradh *handle = ret;
7004e390cabSriastradh ret = 0;
7014e390cabSriastradh } else
7024e390cabSriastradh drm_syncobj_put(syncobj);
7034e390cabSriastradh
7044e390cabSriastradh fdput(f);
7054e390cabSriastradh return ret;
7064e390cabSriastradh }
7074e390cabSriastradh
drm_syncobj_import_sync_file_fence(struct drm_file * file_private,int fd,int handle)7084e390cabSriastradh static int drm_syncobj_import_sync_file_fence(struct drm_file *file_private,
7094e390cabSriastradh int fd, int handle)
7104e390cabSriastradh {
7114e390cabSriastradh struct dma_fence *fence = sync_file_get_fence(fd);
7124e390cabSriastradh struct drm_syncobj *syncobj;
7134e390cabSriastradh
7144e390cabSriastradh if (!fence)
7154e390cabSriastradh return -EINVAL;
7164e390cabSriastradh
7174e390cabSriastradh syncobj = drm_syncobj_find(file_private, handle);
7184e390cabSriastradh if (!syncobj) {
7194e390cabSriastradh dma_fence_put(fence);
7204e390cabSriastradh return -ENOENT;
7214e390cabSriastradh }
7224e390cabSriastradh
7234e390cabSriastradh drm_syncobj_replace_fence(syncobj, fence);
7244e390cabSriastradh dma_fence_put(fence);
7254e390cabSriastradh drm_syncobj_put(syncobj);
7264e390cabSriastradh return 0;
7274e390cabSriastradh }
7284e390cabSriastradh
drm_syncobj_export_sync_file(struct drm_file * file_private,int handle,int * p_fd)7294e390cabSriastradh static int drm_syncobj_export_sync_file(struct drm_file *file_private,
7304e390cabSriastradh int handle, int *p_fd)
7314e390cabSriastradh {
732679fafc5Sriastradh #ifdef __NetBSD__
733679fafc5Sriastradh struct dma_fence *fence;
734679fafc5Sriastradh struct sync_file *sync_file;
735679fafc5Sriastradh struct file *fp = NULL;
736679fafc5Sriastradh int fd = -1;
737679fafc5Sriastradh int ret;
738679fafc5Sriastradh
739679fafc5Sriastradh /* Allocate a file and descriptor. */
740679fafc5Sriastradh /* XXX errno NetBSD->Linux */
741679fafc5Sriastradh ret = -fd_allocfile(&fp, &fd);
742679fafc5Sriastradh if (ret)
743679fafc5Sriastradh goto out;
744679fafc5Sriastradh
745679fafc5Sriastradh /* Find the fence. */
74605319ba3Sriastradh ret = drm_syncobj_find_fence(file_private, handle, 0, 0, &fence);
747679fafc5Sriastradh if (ret)
748679fafc5Sriastradh goto out;
749679fafc5Sriastradh
750679fafc5Sriastradh /* Create the sync file. */
751679fafc5Sriastradh sync_file = sync_file_create(fence, fp);
752679fafc5Sriastradh
753679fafc5Sriastradh /* Release the fence. */
754679fafc5Sriastradh dma_fence_put(fence);
755679fafc5Sriastradh
756679fafc5Sriastradh /* If the sync file creation failed, bail. */
757679fafc5Sriastradh if (sync_file == NULL)
758679fafc5Sriastradh goto out;
759679fafc5Sriastradh
760679fafc5Sriastradh /* Success! */
761679fafc5Sriastradh fd_affix(curproc, fp, fd);
762679fafc5Sriastradh fp = NULL; /* sync_file consumes */
763679fafc5Sriastradh ret = 0;
764679fafc5Sriastradh
765679fafc5Sriastradh out:
766679fafc5Sriastradh /* If anything went wrong and we still have an unused file, abort. */
767679fafc5Sriastradh if (fp != NULL) {
768679fafc5Sriastradh fd_abort(curproc, fp, fd);
769679fafc5Sriastradh fd = -1;
770679fafc5Sriastradh }
771679fafc5Sriastradh
772679fafc5Sriastradh /* Return the descriptor or -1. */
773679fafc5Sriastradh *p_fd = fd;
774679fafc5Sriastradh return ret;
775679fafc5Sriastradh #else
7764e390cabSriastradh int ret;
7774e390cabSriastradh struct dma_fence *fence;
7784e390cabSriastradh struct sync_file *sync_file;
7794e390cabSriastradh int fd = get_unused_fd_flags(O_CLOEXEC);
7804e390cabSriastradh
7814e390cabSriastradh if (fd < 0)
7824e390cabSriastradh return fd;
7834e390cabSriastradh
7844e390cabSriastradh ret = drm_syncobj_find_fence(file_private, handle, 0, 0, &fence);
7854e390cabSriastradh if (ret)
7864e390cabSriastradh goto err_put_fd;
7874e390cabSriastradh
7884e390cabSriastradh sync_file = sync_file_create(fence);
7894e390cabSriastradh
7904e390cabSriastradh dma_fence_put(fence);
7914e390cabSriastradh
7924e390cabSriastradh if (!sync_file) {
7934e390cabSriastradh ret = -EINVAL;
7944e390cabSriastradh goto err_put_fd;
7954e390cabSriastradh }
7964e390cabSriastradh
7974e390cabSriastradh fd_install(fd, sync_file->file);
7984e390cabSriastradh
7994e390cabSriastradh *p_fd = fd;
8004e390cabSriastradh return 0;
8014e390cabSriastradh err_put_fd:
8024e390cabSriastradh put_unused_fd(fd);
8034e390cabSriastradh return ret;
804679fafc5Sriastradh #endif
8054e390cabSriastradh }
8064e390cabSriastradh /**
8074e390cabSriastradh * drm_syncobj_open - initalizes syncobj file-private structures at devnode open time
8084e390cabSriastradh * @file_private: drm file-private structure to set up
8094e390cabSriastradh *
8104e390cabSriastradh * Called at device open time, sets up the structure for handling refcounting
8114e390cabSriastradh * of sync objects.
8124e390cabSriastradh */
8134e390cabSriastradh void
drm_syncobj_open(struct drm_file * file_private)8144e390cabSriastradh drm_syncobj_open(struct drm_file *file_private)
8154e390cabSriastradh {
8164e390cabSriastradh idr_init_base(&file_private->syncobj_idr, 1);
8174e390cabSriastradh spin_lock_init(&file_private->syncobj_table_lock);
8184e390cabSriastradh }
8194e390cabSriastradh
8204e390cabSriastradh static int
drm_syncobj_release_handle(int id,void * ptr,void * data)8214e390cabSriastradh drm_syncobj_release_handle(int id, void *ptr, void *data)
8224e390cabSriastradh {
8234e390cabSriastradh struct drm_syncobj *syncobj = ptr;
8244e390cabSriastradh
8254e390cabSriastradh drm_syncobj_put(syncobj);
8264e390cabSriastradh return 0;
8274e390cabSriastradh }
8284e390cabSriastradh
8294e390cabSriastradh /**
8304e390cabSriastradh * drm_syncobj_release - release file-private sync object resources
8314e390cabSriastradh * @file_private: drm file-private structure to clean up
8324e390cabSriastradh *
8334e390cabSriastradh * Called at close time when the filp is going away.
8344e390cabSriastradh *
8354e390cabSriastradh * Releases any remaining references on objects by this filp.
8364e390cabSriastradh */
8374e390cabSriastradh void
drm_syncobj_release(struct drm_file * file_private)8384e390cabSriastradh drm_syncobj_release(struct drm_file *file_private)
8394e390cabSriastradh {
8404e390cabSriastradh idr_for_each(&file_private->syncobj_idr,
8414e390cabSriastradh &drm_syncobj_release_handle, file_private);
842679fafc5Sriastradh spin_lock_destroy(&file_private->syncobj_table_lock);
8434e390cabSriastradh idr_destroy(&file_private->syncobj_idr);
8444e390cabSriastradh }
8454e390cabSriastradh
8464e390cabSriastradh int
drm_syncobj_create_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)8474e390cabSriastradh drm_syncobj_create_ioctl(struct drm_device *dev, void *data,
8484e390cabSriastradh struct drm_file *file_private)
8494e390cabSriastradh {
8504e390cabSriastradh struct drm_syncobj_create *args = data;
8514e390cabSriastradh
8524e390cabSriastradh if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
8534e390cabSriastradh return -EOPNOTSUPP;
8544e390cabSriastradh
8554e390cabSriastradh /* no valid flags yet */
8564e390cabSriastradh if (args->flags & ~DRM_SYNCOBJ_CREATE_SIGNALED)
8574e390cabSriastradh return -EINVAL;
8584e390cabSriastradh
8594e390cabSriastradh return drm_syncobj_create_as_handle(file_private,
8604e390cabSriastradh &args->handle, args->flags);
8614e390cabSriastradh }
8624e390cabSriastradh
8634e390cabSriastradh int
drm_syncobj_destroy_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)8644e390cabSriastradh drm_syncobj_destroy_ioctl(struct drm_device *dev, void *data,
8654e390cabSriastradh struct drm_file *file_private)
8664e390cabSriastradh {
8674e390cabSriastradh struct drm_syncobj_destroy *args = data;
8684e390cabSriastradh
8694e390cabSriastradh if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
8704e390cabSriastradh return -EOPNOTSUPP;
8714e390cabSriastradh
8724e390cabSriastradh /* make sure padding is empty */
8734e390cabSriastradh if (args->pad)
8744e390cabSriastradh return -EINVAL;
8754e390cabSriastradh return drm_syncobj_destroy(file_private, args->handle);
8764e390cabSriastradh }
8774e390cabSriastradh
8784e390cabSriastradh int
drm_syncobj_handle_to_fd_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)8794e390cabSriastradh drm_syncobj_handle_to_fd_ioctl(struct drm_device *dev, void *data,
8804e390cabSriastradh struct drm_file *file_private)
8814e390cabSriastradh {
8824e390cabSriastradh struct drm_syncobj_handle *args = data;
8834e390cabSriastradh
8844e390cabSriastradh if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
8854e390cabSriastradh return -EOPNOTSUPP;
8864e390cabSriastradh
8874e390cabSriastradh if (args->pad)
8884e390cabSriastradh return -EINVAL;
8894e390cabSriastradh
8904e390cabSriastradh if (args->flags != 0 &&
8914e390cabSriastradh args->flags != DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE)
8924e390cabSriastradh return -EINVAL;
8934e390cabSriastradh
8944e390cabSriastradh if (args->flags & DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE)
8954e390cabSriastradh return drm_syncobj_export_sync_file(file_private, args->handle,
8964e390cabSriastradh &args->fd);
8974e390cabSriastradh
8984e390cabSriastradh return drm_syncobj_handle_to_fd(file_private, args->handle,
8994e390cabSriastradh &args->fd);
9004e390cabSriastradh }
9014e390cabSriastradh
9024e390cabSriastradh int
drm_syncobj_fd_to_handle_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)9034e390cabSriastradh drm_syncobj_fd_to_handle_ioctl(struct drm_device *dev, void *data,
9044e390cabSriastradh struct drm_file *file_private)
9054e390cabSriastradh {
9064e390cabSriastradh struct drm_syncobj_handle *args = data;
9074e390cabSriastradh
9084e390cabSriastradh if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
9094e390cabSriastradh return -EOPNOTSUPP;
9104e390cabSriastradh
9114e390cabSriastradh if (args->pad)
9124e390cabSriastradh return -EINVAL;
9134e390cabSriastradh
9144e390cabSriastradh if (args->flags != 0 &&
9154e390cabSriastradh args->flags != DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE)
9164e390cabSriastradh return -EINVAL;
9174e390cabSriastradh
9184e390cabSriastradh if (args->flags & DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE)
9194e390cabSriastradh return drm_syncobj_import_sync_file_fence(file_private,
9204e390cabSriastradh args->fd,
9214e390cabSriastradh args->handle);
9224e390cabSriastradh
9234e390cabSriastradh return drm_syncobj_fd_to_handle(file_private, args->fd,
9244e390cabSriastradh &args->handle);
9254e390cabSriastradh }
9264e390cabSriastradh
drm_syncobj_transfer_to_timeline(struct drm_file * file_private,struct drm_syncobj_transfer * args)9274e390cabSriastradh static int drm_syncobj_transfer_to_timeline(struct drm_file *file_private,
9284e390cabSriastradh struct drm_syncobj_transfer *args)
9294e390cabSriastradh {
9304e390cabSriastradh struct drm_syncobj *timeline_syncobj = NULL;
9314e390cabSriastradh struct dma_fence *fence;
9324e390cabSriastradh struct dma_fence_chain *chain;
9334e390cabSriastradh int ret;
9344e390cabSriastradh
9354e390cabSriastradh timeline_syncobj = drm_syncobj_find(file_private, args->dst_handle);
9364e390cabSriastradh if (!timeline_syncobj) {
9374e390cabSriastradh return -ENOENT;
9384e390cabSriastradh }
9394e390cabSriastradh ret = drm_syncobj_find_fence(file_private, args->src_handle,
9404e390cabSriastradh args->src_point, args->flags,
9414e390cabSriastradh &fence);
9424e390cabSriastradh if (ret)
9434e390cabSriastradh goto err;
9444e390cabSriastradh chain = kzalloc(sizeof(struct dma_fence_chain), GFP_KERNEL);
9454e390cabSriastradh if (!chain) {
9464e390cabSriastradh ret = -ENOMEM;
9474e390cabSriastradh goto err1;
9484e390cabSriastradh }
9494e390cabSriastradh drm_syncobj_add_point(timeline_syncobj, chain, fence, args->dst_point);
9504e390cabSriastradh err1:
9514e390cabSriastradh dma_fence_put(fence);
9524e390cabSriastradh err:
9534e390cabSriastradh drm_syncobj_put(timeline_syncobj);
9544e390cabSriastradh
9554e390cabSriastradh return ret;
9564e390cabSriastradh }
9574e390cabSriastradh
9584e390cabSriastradh static int
drm_syncobj_transfer_to_binary(struct drm_file * file_private,struct drm_syncobj_transfer * args)9594e390cabSriastradh drm_syncobj_transfer_to_binary(struct drm_file *file_private,
9604e390cabSriastradh struct drm_syncobj_transfer *args)
9614e390cabSriastradh {
9624e390cabSriastradh struct drm_syncobj *binary_syncobj = NULL;
9634e390cabSriastradh struct dma_fence *fence;
9644e390cabSriastradh int ret;
9654e390cabSriastradh
9664e390cabSriastradh binary_syncobj = drm_syncobj_find(file_private, args->dst_handle);
9674e390cabSriastradh if (!binary_syncobj)
9684e390cabSriastradh return -ENOENT;
9694e390cabSriastradh ret = drm_syncobj_find_fence(file_private, args->src_handle,
9704e390cabSriastradh args->src_point, args->flags, &fence);
9714e390cabSriastradh if (ret)
9724e390cabSriastradh goto err;
9734e390cabSriastradh drm_syncobj_replace_fence(binary_syncobj, fence);
9744e390cabSriastradh dma_fence_put(fence);
9754e390cabSriastradh err:
9764e390cabSriastradh drm_syncobj_put(binary_syncobj);
9774e390cabSriastradh
9784e390cabSriastradh return ret;
9794e390cabSriastradh }
9804e390cabSriastradh int
drm_syncobj_transfer_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)9814e390cabSriastradh drm_syncobj_transfer_ioctl(struct drm_device *dev, void *data,
9824e390cabSriastradh struct drm_file *file_private)
9834e390cabSriastradh {
9844e390cabSriastradh struct drm_syncobj_transfer *args = data;
9854e390cabSriastradh int ret;
9864e390cabSriastradh
9874e390cabSriastradh if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
9884e390cabSriastradh return -EOPNOTSUPP;
9894e390cabSriastradh
9904e390cabSriastradh if (args->pad)
9914e390cabSriastradh return -EINVAL;
9924e390cabSriastradh
9934e390cabSriastradh if (args->dst_point)
9944e390cabSriastradh ret = drm_syncobj_transfer_to_timeline(file_private, args);
9954e390cabSriastradh else
9964e390cabSriastradh ret = drm_syncobj_transfer_to_binary(file_private, args);
9974e390cabSriastradh
9984e390cabSriastradh return ret;
9994e390cabSriastradh }
10004e390cabSriastradh
syncobj_wait_fence_func(struct dma_fence * fence,struct dma_fence_cb * cb)10014e390cabSriastradh static void syncobj_wait_fence_func(struct dma_fence *fence,
10024e390cabSriastradh struct dma_fence_cb *cb)
10034e390cabSriastradh {
10044e390cabSriastradh struct syncobj_wait_entry *wait =
10054e390cabSriastradh container_of(cb, struct syncobj_wait_entry, fence_cb);
10064e390cabSriastradh
1007679fafc5Sriastradh #ifdef __NetBSD__
1008679fafc5Sriastradh mutex_enter(wait->lock);
100905319ba3Sriastradh *wait->signalledp = true;
1010679fafc5Sriastradh cv_broadcast(wait->cv);
1011679fafc5Sriastradh mutex_exit(wait->lock);
1012679fafc5Sriastradh #else
10134e390cabSriastradh wake_up_process(wait->task);
1014679fafc5Sriastradh #endif
10154e390cabSriastradh }
10164e390cabSriastradh
syncobj_wait_syncobj_func(struct drm_syncobj * syncobj,struct syncobj_wait_entry * wait)10174e390cabSriastradh static void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj,
10184e390cabSriastradh struct syncobj_wait_entry *wait)
10194e390cabSriastradh {
10204e390cabSriastradh struct dma_fence *fence;
10214e390cabSriastradh
10224e390cabSriastradh /* This happens inside the syncobj lock */
10234e390cabSriastradh fence = rcu_dereference_protected(syncobj->fence,
10244e390cabSriastradh lockdep_is_held(&syncobj->lock));
10254e390cabSriastradh dma_fence_get(fence);
10264e390cabSriastradh if (!fence || dma_fence_chain_find_seqno(&fence, wait->point)) {
10274e390cabSriastradh dma_fence_put(fence);
10284e390cabSriastradh return;
102905319ba3Sriastradh }
103005319ba3Sriastradh
103105319ba3Sriastradh if (!fence) {
10324e390cabSriastradh wait->fence = dma_fence_get_stub();
10334e390cabSriastradh } else {
10344e390cabSriastradh wait->fence = fence;
10354e390cabSriastradh }
10364e390cabSriastradh
1037679fafc5Sriastradh #ifdef __NetBSD__
1038679fafc5Sriastradh KASSERT(spin_is_locked(&syncobj->lock));
1039679fafc5Sriastradh mutex_enter(wait->lock);
1040679fafc5Sriastradh cv_broadcast(wait->cv);
1041679fafc5Sriastradh mutex_exit(wait->lock);
1042679fafc5Sriastradh #else
10434e390cabSriastradh wake_up_process(wait->task);
1044679fafc5Sriastradh #endif
10454e390cabSriastradh list_del_init(&wait->node);
10464e390cabSriastradh }
10474e390cabSriastradh
drm_syncobj_array_wait_timeout(struct drm_syncobj ** syncobjs,void __user * user_points,uint32_t count,uint32_t flags,signed long timeout,uint32_t * idx)10484e390cabSriastradh static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs,
10494e390cabSriastradh void __user *user_points,
10504e390cabSriastradh uint32_t count,
10514e390cabSriastradh uint32_t flags,
10524e390cabSriastradh signed long timeout,
10534e390cabSriastradh uint32_t *idx)
10544e390cabSriastradh {
10554e390cabSriastradh struct syncobj_wait_entry *entries;
10564e390cabSriastradh struct dma_fence *fence;
10574e390cabSriastradh uint64_t *points;
10584e390cabSriastradh uint32_t signaled_count, i;
1059679fafc5Sriastradh #ifdef __NetBSD__
1060679fafc5Sriastradh kmutex_t lock;
1061679fafc5Sriastradh kcondvar_t cv;
106205319ba3Sriastradh bool signalled = false;
106305319ba3Sriastradh int ret;
1064679fafc5Sriastradh mutex_init(&lock, MUTEX_DEFAULT, IPL_VM);
1065679fafc5Sriastradh cv_init(&cv, "drmsynco");
1066679fafc5Sriastradh #endif
10674e390cabSriastradh
10684e390cabSriastradh points = kmalloc_array(count, sizeof(*points), GFP_KERNEL);
10694e390cabSriastradh if (points == NULL)
10704e390cabSriastradh return -ENOMEM;
10714e390cabSriastradh
10724e390cabSriastradh if (!user_points) {
10734e390cabSriastradh memset(points, 0, count * sizeof(uint64_t));
10744e390cabSriastradh
10754e390cabSriastradh } else if (copy_from_user(points, user_points,
10764e390cabSriastradh sizeof(uint64_t) * count)) {
10774e390cabSriastradh timeout = -EFAULT;
10784e390cabSriastradh goto err_free_points;
10794e390cabSriastradh }
10804e390cabSriastradh
10814e390cabSriastradh entries = kcalloc(count, sizeof(*entries), GFP_KERNEL);
10824e390cabSriastradh if (!entries) {
10834e390cabSriastradh timeout = -ENOMEM;
10844e390cabSriastradh goto err_free_points;
10854e390cabSriastradh }
10864e390cabSriastradh /* Walk the list of sync objects and initialize entries. We do
10874e390cabSriastradh * this up-front so that we can properly return -EINVAL if there is
10884e390cabSriastradh * a syncobj with a missing fence and then never have the chance of
10894e390cabSriastradh * returning -EINVAL again.
10904e390cabSriastradh */
10914e390cabSriastradh signaled_count = 0;
10924e390cabSriastradh for (i = 0; i < count; ++i) {
1093679fafc5Sriastradh #ifdef __NetBSD__
1094679fafc5Sriastradh entries[i].lock = &lock;
1095679fafc5Sriastradh entries[i].cv = &cv;
109605319ba3Sriastradh entries[i].signalledp = &signalled;
1097679fafc5Sriastradh #else
10984e390cabSriastradh entries[i].task = current;
1099679fafc5Sriastradh #endif
11004e390cabSriastradh entries[i].point = points[i];
11014e390cabSriastradh fence = drm_syncobj_fence_get(syncobjs[i]);
11024e390cabSriastradh if (!fence || dma_fence_chain_find_seqno(&fence, points[i])) {
11034e390cabSriastradh dma_fence_put(fence);
11044e390cabSriastradh if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) {
11054e390cabSriastradh continue;
11064e390cabSriastradh } else {
11074e390cabSriastradh timeout = -EINVAL;
11084e390cabSriastradh goto cleanup_entries;
11094e390cabSriastradh }
11104e390cabSriastradh }
11114e390cabSriastradh
11124e390cabSriastradh if (fence)
11134e390cabSriastradh entries[i].fence = fence;
11144e390cabSriastradh else
11154e390cabSriastradh entries[i].fence = dma_fence_get_stub();
11164e390cabSriastradh
11174e390cabSriastradh if ((flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE) ||
11184e390cabSriastradh dma_fence_is_signaled(entries[i].fence)) {
11194e390cabSriastradh if (signaled_count == 0 && idx)
11204e390cabSriastradh *idx = i;
11214e390cabSriastradh signaled_count++;
11224e390cabSriastradh }
11234e390cabSriastradh }
11244e390cabSriastradh
11254e390cabSriastradh if (signaled_count == count ||
11264e390cabSriastradh (signaled_count > 0 &&
11274e390cabSriastradh !(flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL)))
11284e390cabSriastradh goto cleanup_entries;
11294e390cabSriastradh
11304e390cabSriastradh /* There's a very annoying laxness in the dma_fence API here, in
11314e390cabSriastradh * that backends are not required to automatically report when a
11324e390cabSriastradh * fence is signaled prior to fence->ops->enable_signaling() being
11334e390cabSriastradh * called. So here if we fail to match signaled_count, we need to
11344e390cabSriastradh * fallthough and try a 0 timeout wait!
11354e390cabSriastradh */
11364e390cabSriastradh
11374e390cabSriastradh if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) {
11384e390cabSriastradh for (i = 0; i < count; ++i)
11394e390cabSriastradh drm_syncobj_fence_add_wait(syncobjs[i], &entries[i]);
11404e390cabSriastradh }
11414e390cabSriastradh
11424e390cabSriastradh do {
1143679fafc5Sriastradh #ifndef __NetBSD__
11444e390cabSriastradh set_current_state(TASK_INTERRUPTIBLE);
1145679fafc5Sriastradh #endif
11464e390cabSriastradh
11474e390cabSriastradh signaled_count = 0;
11484e390cabSriastradh for (i = 0; i < count; ++i) {
11494e390cabSriastradh fence = entries[i].fence;
11504e390cabSriastradh if (!fence)
11514e390cabSriastradh continue;
11524e390cabSriastradh
11534e390cabSriastradh if ((flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE) ||
11544e390cabSriastradh dma_fence_is_signaled(fence) ||
11554e390cabSriastradh (!entries[i].fence_cb.func &&
11564e390cabSriastradh dma_fence_add_callback(fence,
11574e390cabSriastradh &entries[i].fence_cb,
11584e390cabSriastradh syncobj_wait_fence_func))) {
11594e390cabSriastradh /* The fence has been signaled */
11604e390cabSriastradh if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL) {
11614e390cabSriastradh signaled_count++;
11624e390cabSriastradh } else {
11634e390cabSriastradh if (idx)
11644e390cabSriastradh *idx = i;
11654e390cabSriastradh goto done_waiting;
11664e390cabSriastradh }
11674e390cabSriastradh }
11684e390cabSriastradh }
11694e390cabSriastradh
11704e390cabSriastradh if (signaled_count == count)
11714e390cabSriastradh goto done_waiting;
11724e390cabSriastradh
11734e390cabSriastradh if (timeout == 0) {
11744e390cabSriastradh timeout = -ETIME;
11754e390cabSriastradh goto done_waiting;
11764e390cabSriastradh }
11774e390cabSriastradh
1178679fafc5Sriastradh #ifdef __NetBSD__
1179*e88c3a9bSriastradh mutex_spin_enter(&lock);
1180*e88c3a9bSriastradh if (signalled) {
1181679fafc5Sriastradh ret = 0;
1182*e88c3a9bSriastradh } else {
1183*e88c3a9bSriastradh unsigned start, end;
1184*e88c3a9bSriastradh
1185*e88c3a9bSriastradh start = getticks();
1186*e88c3a9bSriastradh /* XXX errno NetBSD->Linux */
1187*e88c3a9bSriastradh ret = -cv_timedwait_sig(&cv, &lock,
1188*e88c3a9bSriastradh MIN(timeout, INT_MAX/2));
1189*e88c3a9bSriastradh end = getticks();
1190*e88c3a9bSriastradh timeout -= MIN(timeout, end - start);
1191*e88c3a9bSriastradh }
1192*e88c3a9bSriastradh mutex_spin_exit(&lock);
1193*e88c3a9bSriastradh KASSERTMSG((ret == 0 || ret == -EINTR || ret == -ERESTART ||
1194*e88c3a9bSriastradh ret == -EWOULDBLOCK), "ret=%d", ret);
1195*e88c3a9bSriastradh if (ret == -EINTR || ret == -ERESTART) {
119605319ba3Sriastradh timeout = -ERESTARTSYS;
119705319ba3Sriastradh goto done_waiting;
1198*e88c3a9bSriastradh } else if (ret == -EWOULDBLOCK) {
1199*e88c3a9bSriastradh /* Poll fences once more, then exit. */
1200*e88c3a9bSriastradh timeout = 0;
1201*e88c3a9bSriastradh } else {
1202*e88c3a9bSriastradh KASSERT(ret == 0);
1203679fafc5Sriastradh }
1204679fafc5Sriastradh #else
12054e390cabSriastradh if (signal_pending(current)) {
12064e390cabSriastradh timeout = -ERESTARTSYS;
12074e390cabSriastradh goto done_waiting;
12084e390cabSriastradh }
12094e390cabSriastradh
12104e390cabSriastradh timeout = schedule_timeout(timeout);
1211679fafc5Sriastradh #endif
12124e390cabSriastradh } while (1);
12134e390cabSriastradh
12144e390cabSriastradh done_waiting:
1215679fafc5Sriastradh #ifndef __NetBSD__
12164e390cabSriastradh __set_current_state(TASK_RUNNING);
1217679fafc5Sriastradh #endif
12184e390cabSriastradh
12194e390cabSriastradh cleanup_entries:
12204e390cabSriastradh for (i = 0; i < count; ++i) {
12214e390cabSriastradh drm_syncobj_remove_wait(syncobjs[i], &entries[i]);
12224e390cabSriastradh if (entries[i].fence_cb.func)
12234e390cabSriastradh dma_fence_remove_callback(entries[i].fence,
12244e390cabSriastradh &entries[i].fence_cb);
12254e390cabSriastradh dma_fence_put(entries[i].fence);
12264e390cabSriastradh }
12274e390cabSriastradh kfree(entries);
12284e390cabSriastradh
12294e390cabSriastradh err_free_points:
12304e390cabSriastradh kfree(points);
123105319ba3Sriastradh #ifdef __NetBSD__
1232679fafc5Sriastradh cv_destroy(&cv);
1233679fafc5Sriastradh mutex_destroy(&lock);
123405319ba3Sriastradh #endif
12354e390cabSriastradh
12364e390cabSriastradh return timeout;
12374e390cabSriastradh }
12384e390cabSriastradh
12394e390cabSriastradh /**
12404e390cabSriastradh * drm_timeout_abs_to_jiffies - calculate jiffies timeout from absolute value
12414e390cabSriastradh *
12424e390cabSriastradh * @timeout_nsec: timeout nsec component in ns, 0 for poll
12434e390cabSriastradh *
12444e390cabSriastradh * Calculate the timeout in jiffies from an absolute time in sec/nsec.
12454e390cabSriastradh */
drm_timeout_abs_to_jiffies(int64_t timeout_nsec)12464e390cabSriastradh signed long drm_timeout_abs_to_jiffies(int64_t timeout_nsec)
12474e390cabSriastradh {
12484e390cabSriastradh ktime_t abs_timeout, now;
12494e390cabSriastradh u64 timeout_ns, timeout_jiffies64;
12504e390cabSriastradh
12514e390cabSriastradh /* make 0 timeout means poll - absolute 0 doesn't seem valid */
12524e390cabSriastradh if (timeout_nsec == 0)
12534e390cabSriastradh return 0;
12544e390cabSriastradh
12554e390cabSriastradh abs_timeout = ns_to_ktime(timeout_nsec);
12564e390cabSriastradh now = ktime_get();
12574e390cabSriastradh
12584e390cabSriastradh if (!ktime_after(abs_timeout, now))
12594e390cabSriastradh return 0;
12604e390cabSriastradh
12614e390cabSriastradh timeout_ns = ktime_to_ns(ktime_sub(abs_timeout, now));
12624e390cabSriastradh
12634e390cabSriastradh timeout_jiffies64 = nsecs_to_jiffies64(timeout_ns);
12644e390cabSriastradh /* clamp timeout to avoid infinite timeout */
12654e390cabSriastradh if (timeout_jiffies64 >= MAX_SCHEDULE_TIMEOUT - 1)
12664e390cabSriastradh return MAX_SCHEDULE_TIMEOUT - 1;
12674e390cabSriastradh
12684e390cabSriastradh return timeout_jiffies64 + 1;
12694e390cabSriastradh }
12704e390cabSriastradh EXPORT_SYMBOL(drm_timeout_abs_to_jiffies);
12714e390cabSriastradh
drm_syncobj_array_wait(struct drm_device * dev,struct drm_file * file_private,struct drm_syncobj_wait * wait,struct drm_syncobj_timeline_wait * timeline_wait,struct drm_syncobj ** syncobjs,bool timeline)12724e390cabSriastradh static int drm_syncobj_array_wait(struct drm_device *dev,
12734e390cabSriastradh struct drm_file *file_private,
12744e390cabSriastradh struct drm_syncobj_wait *wait,
12754e390cabSriastradh struct drm_syncobj_timeline_wait *timeline_wait,
12764e390cabSriastradh struct drm_syncobj **syncobjs, bool timeline)
12774e390cabSriastradh {
12784e390cabSriastradh signed long timeout = 0;
12794e390cabSriastradh uint32_t first = ~0;
12804e390cabSriastradh
12814e390cabSriastradh if (!timeline) {
12824e390cabSriastradh timeout = drm_timeout_abs_to_jiffies(wait->timeout_nsec);
12834e390cabSriastradh timeout = drm_syncobj_array_wait_timeout(syncobjs,
12844e390cabSriastradh NULL,
12854e390cabSriastradh wait->count_handles,
12864e390cabSriastradh wait->flags,
12874e390cabSriastradh timeout, &first);
12884e390cabSriastradh if (timeout < 0)
12894e390cabSriastradh return timeout;
12904e390cabSriastradh wait->first_signaled = first;
12914e390cabSriastradh } else {
12924e390cabSriastradh timeout = drm_timeout_abs_to_jiffies(timeline_wait->timeout_nsec);
12934e390cabSriastradh timeout = drm_syncobj_array_wait_timeout(syncobjs,
12944e390cabSriastradh u64_to_user_ptr(timeline_wait->points),
12954e390cabSriastradh timeline_wait->count_handles,
12964e390cabSriastradh timeline_wait->flags,
12974e390cabSriastradh timeout, &first);
12984e390cabSriastradh if (timeout < 0)
12994e390cabSriastradh return timeout;
13004e390cabSriastradh timeline_wait->first_signaled = first;
13014e390cabSriastradh }
13024e390cabSriastradh return 0;
13034e390cabSriastradh }
13044e390cabSriastradh
drm_syncobj_array_find(struct drm_file * file_private,void __user * user_handles,uint32_t count_handles,struct drm_syncobj *** syncobjs_out)13054e390cabSriastradh static int drm_syncobj_array_find(struct drm_file *file_private,
13064e390cabSriastradh void __user *user_handles,
13074e390cabSriastradh uint32_t count_handles,
13084e390cabSriastradh struct drm_syncobj ***syncobjs_out)
13094e390cabSriastradh {
13104e390cabSriastradh uint32_t i, *handles;
13114e390cabSriastradh struct drm_syncobj **syncobjs;
13124e390cabSriastradh int ret;
13134e390cabSriastradh
13144e390cabSriastradh handles = kmalloc_array(count_handles, sizeof(*handles), GFP_KERNEL);
13154e390cabSriastradh if (handles == NULL)
13164e390cabSriastradh return -ENOMEM;
13174e390cabSriastradh
13184e390cabSriastradh if (copy_from_user(handles, user_handles,
13194e390cabSriastradh sizeof(uint32_t) * count_handles)) {
13204e390cabSriastradh ret = -EFAULT;
13214e390cabSriastradh goto err_free_handles;
13224e390cabSriastradh }
13234e390cabSriastradh
13244e390cabSriastradh syncobjs = kmalloc_array(count_handles, sizeof(*syncobjs), GFP_KERNEL);
13254e390cabSriastradh if (syncobjs == NULL) {
13264e390cabSriastradh ret = -ENOMEM;
13274e390cabSriastradh goto err_free_handles;
13284e390cabSriastradh }
13294e390cabSriastradh
13304e390cabSriastradh for (i = 0; i < count_handles; i++) {
13314e390cabSriastradh syncobjs[i] = drm_syncobj_find(file_private, handles[i]);
13324e390cabSriastradh if (!syncobjs[i]) {
13334e390cabSriastradh ret = -ENOENT;
13344e390cabSriastradh goto err_put_syncobjs;
13354e390cabSriastradh }
13364e390cabSriastradh }
13374e390cabSriastradh
13384e390cabSriastradh kfree(handles);
13394e390cabSriastradh *syncobjs_out = syncobjs;
13404e390cabSriastradh return 0;
13414e390cabSriastradh
13424e390cabSriastradh err_put_syncobjs:
13434e390cabSriastradh while (i-- > 0)
13444e390cabSriastradh drm_syncobj_put(syncobjs[i]);
13454e390cabSriastradh kfree(syncobjs);
13464e390cabSriastradh err_free_handles:
13474e390cabSriastradh kfree(handles);
13484e390cabSriastradh
13494e390cabSriastradh return ret;
13504e390cabSriastradh }
13514e390cabSriastradh
drm_syncobj_array_free(struct drm_syncobj ** syncobjs,uint32_t count)13524e390cabSriastradh static void drm_syncobj_array_free(struct drm_syncobj **syncobjs,
13534e390cabSriastradh uint32_t count)
13544e390cabSriastradh {
13554e390cabSriastradh uint32_t i;
13564e390cabSriastradh for (i = 0; i < count; i++)
13574e390cabSriastradh drm_syncobj_put(syncobjs[i]);
13584e390cabSriastradh kfree(syncobjs);
13594e390cabSriastradh }
13604e390cabSriastradh
13614e390cabSriastradh int
drm_syncobj_wait_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)13624e390cabSriastradh drm_syncobj_wait_ioctl(struct drm_device *dev, void *data,
13634e390cabSriastradh struct drm_file *file_private)
13644e390cabSriastradh {
13654e390cabSriastradh struct drm_syncobj_wait *args = data;
13664e390cabSriastradh struct drm_syncobj **syncobjs;
13674e390cabSriastradh int ret = 0;
13684e390cabSriastradh
13694e390cabSriastradh if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
13704e390cabSriastradh return -EOPNOTSUPP;
13714e390cabSriastradh
13724e390cabSriastradh if (args->flags & ~(DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL |
13734e390cabSriastradh DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT))
13744e390cabSriastradh return -EINVAL;
13754e390cabSriastradh
13764e390cabSriastradh if (args->count_handles == 0)
13774e390cabSriastradh return -EINVAL;
13784e390cabSriastradh
13794e390cabSriastradh ret = drm_syncobj_array_find(file_private,
13804e390cabSriastradh u64_to_user_ptr(args->handles),
13814e390cabSriastradh args->count_handles,
13824e390cabSriastradh &syncobjs);
13834e390cabSriastradh if (ret < 0)
13844e390cabSriastradh return ret;
13854e390cabSriastradh
13864e390cabSriastradh ret = drm_syncobj_array_wait(dev, file_private,
13874e390cabSriastradh args, NULL, syncobjs, false);
13884e390cabSriastradh
13894e390cabSriastradh drm_syncobj_array_free(syncobjs, args->count_handles);
13904e390cabSriastradh
13914e390cabSriastradh return ret;
13924e390cabSriastradh }
13934e390cabSriastradh
13944e390cabSriastradh int
drm_syncobj_timeline_wait_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)13954e390cabSriastradh drm_syncobj_timeline_wait_ioctl(struct drm_device *dev, void *data,
13964e390cabSriastradh struct drm_file *file_private)
13974e390cabSriastradh {
13984e390cabSriastradh struct drm_syncobj_timeline_wait *args = data;
13994e390cabSriastradh struct drm_syncobj **syncobjs;
14004e390cabSriastradh int ret = 0;
14014e390cabSriastradh
14024e390cabSriastradh if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
14034e390cabSriastradh return -EOPNOTSUPP;
14044e390cabSriastradh
14054e390cabSriastradh if (args->flags & ~(DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL |
14064e390cabSriastradh DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
14074e390cabSriastradh DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE))
14084e390cabSriastradh return -EINVAL;
14094e390cabSriastradh
14104e390cabSriastradh if (args->count_handles == 0)
14114e390cabSriastradh return -EINVAL;
14124e390cabSriastradh
14134e390cabSriastradh ret = drm_syncobj_array_find(file_private,
14144e390cabSriastradh u64_to_user_ptr(args->handles),
14154e390cabSriastradh args->count_handles,
14164e390cabSriastradh &syncobjs);
14174e390cabSriastradh if (ret < 0)
14184e390cabSriastradh return ret;
14194e390cabSriastradh
14204e390cabSriastradh ret = drm_syncobj_array_wait(dev, file_private,
14214e390cabSriastradh NULL, args, syncobjs, true);
14224e390cabSriastradh
14234e390cabSriastradh drm_syncobj_array_free(syncobjs, args->count_handles);
14244e390cabSriastradh
14254e390cabSriastradh return ret;
14264e390cabSriastradh }
14274e390cabSriastradh
14284e390cabSriastradh
14294e390cabSriastradh int
drm_syncobj_reset_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)14304e390cabSriastradh drm_syncobj_reset_ioctl(struct drm_device *dev, void *data,
14314e390cabSriastradh struct drm_file *file_private)
14324e390cabSriastradh {
14334e390cabSriastradh struct drm_syncobj_array *args = data;
14344e390cabSriastradh struct drm_syncobj **syncobjs;
14354e390cabSriastradh uint32_t i;
14364e390cabSriastradh int ret;
14374e390cabSriastradh
14384e390cabSriastradh if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
14394e390cabSriastradh return -EOPNOTSUPP;
14404e390cabSriastradh
14414e390cabSriastradh if (args->pad != 0)
14424e390cabSriastradh return -EINVAL;
14434e390cabSriastradh
14444e390cabSriastradh if (args->count_handles == 0)
14454e390cabSriastradh return -EINVAL;
14464e390cabSriastradh
14474e390cabSriastradh ret = drm_syncobj_array_find(file_private,
14484e390cabSriastradh u64_to_user_ptr(args->handles),
14494e390cabSriastradh args->count_handles,
14504e390cabSriastradh &syncobjs);
14514e390cabSriastradh if (ret < 0)
14524e390cabSriastradh return ret;
14534e390cabSriastradh
14544e390cabSriastradh for (i = 0; i < args->count_handles; i++)
14554e390cabSriastradh drm_syncobj_replace_fence(syncobjs[i], NULL);
14564e390cabSriastradh
14574e390cabSriastradh drm_syncobj_array_free(syncobjs, args->count_handles);
14584e390cabSriastradh
14594e390cabSriastradh return 0;
14604e390cabSriastradh }
14614e390cabSriastradh
14624e390cabSriastradh int
drm_syncobj_signal_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)14634e390cabSriastradh drm_syncobj_signal_ioctl(struct drm_device *dev, void *data,
14644e390cabSriastradh struct drm_file *file_private)
14654e390cabSriastradh {
14664e390cabSriastradh struct drm_syncobj_array *args = data;
14674e390cabSriastradh struct drm_syncobj **syncobjs;
14684e390cabSriastradh uint32_t i;
14694e390cabSriastradh int ret;
14704e390cabSriastradh
14714e390cabSriastradh if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
14724e390cabSriastradh return -EOPNOTSUPP;
14734e390cabSriastradh
14744e390cabSriastradh if (args->pad != 0)
14754e390cabSriastradh return -EINVAL;
14764e390cabSriastradh
14774e390cabSriastradh if (args->count_handles == 0)
14784e390cabSriastradh return -EINVAL;
14794e390cabSriastradh
14804e390cabSriastradh ret = drm_syncobj_array_find(file_private,
14814e390cabSriastradh u64_to_user_ptr(args->handles),
14824e390cabSriastradh args->count_handles,
14834e390cabSriastradh &syncobjs);
14844e390cabSriastradh if (ret < 0)
14854e390cabSriastradh return ret;
14864e390cabSriastradh
14874e390cabSriastradh for (i = 0; i < args->count_handles; i++)
14884e390cabSriastradh drm_syncobj_assign_null_handle(syncobjs[i]);
14894e390cabSriastradh
14904e390cabSriastradh drm_syncobj_array_free(syncobjs, args->count_handles);
14914e390cabSriastradh
14924e390cabSriastradh return ret;
14934e390cabSriastradh }
14944e390cabSriastradh
14954e390cabSriastradh int
drm_syncobj_timeline_signal_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)14964e390cabSriastradh drm_syncobj_timeline_signal_ioctl(struct drm_device *dev, void *data,
14974e390cabSriastradh struct drm_file *file_private)
14984e390cabSriastradh {
14994e390cabSriastradh struct drm_syncobj_timeline_array *args = data;
15004e390cabSriastradh struct drm_syncobj **syncobjs;
15014e390cabSriastradh struct dma_fence_chain **chains;
15024e390cabSriastradh uint64_t *points;
15034e390cabSriastradh uint32_t i, j;
15044e390cabSriastradh int ret;
15054e390cabSriastradh
15064e390cabSriastradh if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
15074e390cabSriastradh return -EOPNOTSUPP;
15084e390cabSriastradh
15094e390cabSriastradh if (args->flags != 0)
15104e390cabSriastradh return -EINVAL;
15114e390cabSriastradh
15124e390cabSriastradh if (args->count_handles == 0)
15134e390cabSriastradh return -EINVAL;
15144e390cabSriastradh
15154e390cabSriastradh ret = drm_syncobj_array_find(file_private,
15164e390cabSriastradh u64_to_user_ptr(args->handles),
15174e390cabSriastradh args->count_handles,
15184e390cabSriastradh &syncobjs);
15194e390cabSriastradh if (ret < 0)
15204e390cabSriastradh return ret;
15214e390cabSriastradh
15224e390cabSriastradh points = kmalloc_array(args->count_handles, sizeof(*points),
15234e390cabSriastradh GFP_KERNEL);
15244e390cabSriastradh if (!points) {
15254e390cabSriastradh ret = -ENOMEM;
15264e390cabSriastradh goto out;
15274e390cabSriastradh }
15284e390cabSriastradh if (!u64_to_user_ptr(args->points)) {
15294e390cabSriastradh memset(points, 0, args->count_handles * sizeof(uint64_t));
15304e390cabSriastradh } else if (copy_from_user(points, u64_to_user_ptr(args->points),
15314e390cabSriastradh sizeof(uint64_t) * args->count_handles)) {
15324e390cabSriastradh ret = -EFAULT;
15334e390cabSriastradh goto err_points;
15344e390cabSriastradh }
15354e390cabSriastradh
15364e390cabSriastradh chains = kmalloc_array(args->count_handles, sizeof(void *), GFP_KERNEL);
15374e390cabSriastradh if (!chains) {
15384e390cabSriastradh ret = -ENOMEM;
15394e390cabSriastradh goto err_points;
15404e390cabSriastradh }
15414e390cabSriastradh for (i = 0; i < args->count_handles; i++) {
15424e390cabSriastradh chains[i] = kzalloc(sizeof(struct dma_fence_chain), GFP_KERNEL);
15434e390cabSriastradh if (!chains[i]) {
15444e390cabSriastradh for (j = 0; j < i; j++)
15454e390cabSriastradh kfree(chains[j]);
15464e390cabSriastradh ret = -ENOMEM;
15474e390cabSriastradh goto err_chains;
15484e390cabSriastradh }
15494e390cabSriastradh }
15504e390cabSriastradh
15514e390cabSriastradh for (i = 0; i < args->count_handles; i++) {
15524e390cabSriastradh struct dma_fence *fence = dma_fence_get_stub();
15534e390cabSriastradh
15544e390cabSriastradh drm_syncobj_add_point(syncobjs[i], chains[i],
15554e390cabSriastradh fence, points[i]);
15564e390cabSriastradh dma_fence_put(fence);
15574e390cabSriastradh }
15584e390cabSriastradh err_chains:
15594e390cabSriastradh kfree(chains);
15604e390cabSriastradh err_points:
15614e390cabSriastradh kfree(points);
15624e390cabSriastradh out:
15634e390cabSriastradh drm_syncobj_array_free(syncobjs, args->count_handles);
15644e390cabSriastradh
15654e390cabSriastradh return ret;
15664e390cabSriastradh }
15674e390cabSriastradh
drm_syncobj_query_ioctl(struct drm_device * dev,void * data,struct drm_file * file_private)15684e390cabSriastradh int drm_syncobj_query_ioctl(struct drm_device *dev, void *data,
15694e390cabSriastradh struct drm_file *file_private)
15704e390cabSriastradh {
15714e390cabSriastradh struct drm_syncobj_timeline_array *args = data;
15724e390cabSriastradh struct drm_syncobj **syncobjs;
15734e390cabSriastradh uint64_t __user *points = u64_to_user_ptr(args->points);
15744e390cabSriastradh uint32_t i;
15754e390cabSriastradh int ret;
15764e390cabSriastradh
15774e390cabSriastradh if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE))
15784e390cabSriastradh return -EOPNOTSUPP;
15794e390cabSriastradh
15804e390cabSriastradh if (args->flags & ~DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED)
15814e390cabSriastradh return -EINVAL;
15824e390cabSriastradh
15834e390cabSriastradh if (args->count_handles == 0)
15844e390cabSriastradh return -EINVAL;
15854e390cabSriastradh
15864e390cabSriastradh ret = drm_syncobj_array_find(file_private,
15874e390cabSriastradh u64_to_user_ptr(args->handles),
15884e390cabSriastradh args->count_handles,
15894e390cabSriastradh &syncobjs);
15904e390cabSriastradh if (ret < 0)
15914e390cabSriastradh return ret;
15924e390cabSriastradh
15934e390cabSriastradh for (i = 0; i < args->count_handles; i++) {
15944e390cabSriastradh struct dma_fence_chain *chain;
15954e390cabSriastradh struct dma_fence *fence;
15964e390cabSriastradh uint64_t point;
15974e390cabSriastradh
15984e390cabSriastradh fence = drm_syncobj_fence_get(syncobjs[i]);
15994e390cabSriastradh chain = to_dma_fence_chain(fence);
16004e390cabSriastradh if (chain) {
16014e390cabSriastradh struct dma_fence *iter, *last_signaled =
16024e390cabSriastradh dma_fence_get(fence);
16034e390cabSriastradh
16044e390cabSriastradh if (args->flags &
16054e390cabSriastradh DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED) {
16064e390cabSriastradh point = fence->seqno;
16074e390cabSriastradh } else {
16084e390cabSriastradh dma_fence_chain_for_each(iter, fence) {
16094e390cabSriastradh if (iter->context != fence->context) {
16104e390cabSriastradh dma_fence_put(iter);
16114e390cabSriastradh /* It is most likely that timeline has
16124e390cabSriastradh * unorder points. */
16134e390cabSriastradh break;
16144e390cabSriastradh }
16154e390cabSriastradh dma_fence_put(last_signaled);
16164e390cabSriastradh last_signaled = dma_fence_get(iter);
16174e390cabSriastradh }
16184e390cabSriastradh point = dma_fence_is_signaled(last_signaled) ?
16194e390cabSriastradh last_signaled->seqno :
16204e390cabSriastradh to_dma_fence_chain(last_signaled)->prev_seqno;
16214e390cabSriastradh }
16224e390cabSriastradh dma_fence_put(last_signaled);
16234e390cabSriastradh } else {
16244e390cabSriastradh point = 0;
16254e390cabSriastradh }
16264e390cabSriastradh dma_fence_put(fence);
16274e390cabSriastradh ret = copy_to_user(&points[i], &point, sizeof(uint64_t));
16284e390cabSriastradh ret = ret ? -EFAULT : 0;
16294e390cabSriastradh if (ret)
16304e390cabSriastradh break;
16314e390cabSriastradh }
16324e390cabSriastradh drm_syncobj_array_free(syncobjs, args->count_handles);
16334e390cabSriastradh
16344e390cabSriastradh return ret;
16354e390cabSriastradh }
1636