/*
* Copyright 2017 Red Hat
* Parts ported from amdgpu (fence wait code).
* Copyright 2016 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Authors:
*
*/
/**
* DOC: Overview
*
* DRM synchronisation objects (syncobj, see struct &drm_syncobj) provide a
* container for a synchronization primitive which can be used by userspace
* to explicitly synchronize GPU commands, can be shared between userspace
* processes, and can be shared between different DRM drivers.
* Their primary use-case is to implement Vulkan fences and semaphores.
* The syncobj userspace API provides ioctls for several operations:
*
* - Creation and destruction of syncobjs
* - Import and export of syncobjs to/from a syncobj file descriptor
* - Import and export a syncobj's underlying fence to/from a sync file
* - Reset a syncobj (set its fence to NULL)
* - Signal a syncobj (set a trivially signaled fence)
* - Wait for a syncobj's fence to appear and be signaled
*
* The syncobj userspace API also provides operations to manipulate a syncobj
* in terms of a timeline of struct &dma_fence_chain rather than a single
* struct &dma_fence, through the following operations:
*
* - Signal a given point on the timeline
* - Wait for a given point to appear and/or be signaled
* - Import and export from/to a given point of a timeline
*
* At it's core, a syncobj is simply a wrapper around a pointer to a struct
* &dma_fence which may be NULL.
* When a syncobj is first created, its pointer is either NULL or a pointer
* to an already signaled fence depending on whether the
* &DRM_SYNCOBJ_CREATE_SIGNALED flag is passed to
* &DRM_IOCTL_SYNCOBJ_CREATE.
*
* If the syncobj is considered as a binary (its state is either signaled or
* unsignaled) primitive, when GPU work is enqueued in a DRM driver to signal
* the syncobj, the syncobj's fence is replaced with a fence which will be
* signaled by the completion of that work.
* If the syncobj is considered as a timeline primitive, when GPU work is
* enqueued in a DRM driver to signal the a given point of the syncobj, a new
* struct &dma_fence_chain pointing to the DRM driver's fence and also
* pointing to the previous fence that was in the syncobj. The new struct
* &dma_fence_chain fence replace the syncobj's fence and will be signaled by
* completion of the DRM driver's work and also any work associated with the
* fence previously in the syncobj.
*
* When GPU work which waits on a syncobj is enqueued in a DRM driver, at the
* time the work is enqueued, it waits on the syncobj's fence before
* submitting the work to hardware. That fence is either :
*
* - The syncobj's current fence if the syncobj is considered as a binary
* primitive.
* - The struct &dma_fence associated with a given point if the syncobj is
* considered as a timeline primitive.
*
* If the syncobj's fence is NULL or not present in the syncobj's timeline,
* the enqueue operation is expected to fail.
*
* With binary syncobj, all manipulation of the syncobjs's fence happens in
* terms of the current fence at the time the ioctl is called by userspace
* regardless of whether that operation is an immediate host-side operation
* (signal or reset) or or an operation which is enqueued in some driver
* queue. &DRM_IOCTL_SYNCOBJ_RESET and &DRM_IOCTL_SYNCOBJ_SIGNAL can be used
* to manipulate a syncobj from the host by resetting its pointer to NULL or
* setting its pointer to a fence which is already signaled.
*
* With a timeline syncobj, all manipulation of the synobj's fence happens in
* terms of a u64 value referring to point in the timeline. See
* dma_fence_chain_find_seqno() to see how a given point is found in the
* timeline.
*
* Note that applications should be careful to always use timeline set of
* ioctl() when dealing with syncobj considered as timeline. Using a binary
* set of ioctl() with a syncobj considered as timeline could result incorrect
* synchronization. The use of binary syncobj is supported through the
* timeline set of ioctl() by using a point value of 0, this will reproduce
* the behavior of the binary set of ioctl() (for example replace the
* syncobj's fence when signaling).
*
*
* Host-side wait on syncobjs
* --------------------------
*
* &DRM_IOCTL_SYNCOBJ_WAIT takes an array of syncobj handles and does a
* host-side wait on all of the syncobj fences simultaneously.
* If &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL is set, the wait ioctl will wait on
* all of the syncobj fences to be signaled before it returns.
* Otherwise, it returns once at least one syncobj fence has been signaled
* and the index of a signaled fence is written back to the client.
*
* Unlike the enqueued GPU work dependencies which fail if they see a NULL
* fence in a syncobj, if &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT is set,
* the host-side wait will first wait for the syncobj to receive a non-NULL
* fence and then wait on that fence.
* If &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT is not set and any one of the
* syncobjs in the array has a NULL fence, -EINVAL will be returned.
* Assuming the syncobj starts off with a NULL fence, this allows a client
* to do a host wait in one thread (or process) which waits on GPU work
* submitted
|