/*
* 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) are
* persistent objects that contain an optional fence. The fence can be updated
* with a new fence, or be NULL.
*
* syncobj's can be waited upon, where it will wait for the underlying
* fence.
*
* syncobj's can be export to fd's and back, these fd's are opaque and
* have no other use case, except passing the syncobj between processes.
*
* Their primary use-case is to implement Vulkan fences and semaphores.
*
* syncobj have a kref reference count, but also have an optional file.
* The file is only created once the syncobj is exported.
* The file takes a reference on the kref.
*/
#include <drm/drmP.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/anon_inodes.h>
#include <linux/sync_file.h>
#include <linux/sched/signal.h>
#include "drm_internal.h"
#include <drm/drm_syncobj.h>
/* merge normal syncobj to timeline syncobj, the point interval is 1 */
#define DRM_SYNCOBJ_BINARY_POINT 1
struct drm_syncobj_stub_fence {
struct dma_fence base;
spinlock_t lock;
};
static const char *drm_syncobj_stub_fence_get_name(struct dma_fence *fence)
{
return "syncobjstub";
}
static const struct dma_fence_ops drm_syncobj_stub_fence_ops = {
.get_driver_name = drm_syncobj_stub_fence_get_name,
.get_timeline_name = drm_syncobj_stub_fence_get_name,
};
struct drm_syncobj_signal_pt {
struct dma_fence_array *fence_array;
u64 value;
struct list_head list;
};
/**
* drm_syncobj_find - lookup and reference a sync object.
* @file_private: drm file private pointer
* @handle: sync object handle to lookup.
*
* Returns a reference to the syncobj pointed to by handle or NULL. The
* reference must be released by calling drm_syncobj_put().
*/
struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private,
u32 handle)
{
struct drm_syncobj *syncobj;
spin_lock(&file_private->syncobj_table_lock);
/* Check if we currently have a reference on the object */
syncobj = idr_find(&file_private->syncobj_idr, handle);
if (syncobj)
drm_syncobj_get(syncobj);
spin_unlock(&file_private->syncobj_table_lock);
return syncobj;
}
EXPORT_SYMBOL(drm_syncobj_find);
static struct dma_fence
*drm_syncobj_find_signal_pt_for_point(struct drm_syncobj *syncobj,
uint64_t point)
{
struct drm_syncobj_signal_pt *signal_pt;
if ((syncobj->type == DRM_SYNCOBJ_TYPE_TIMELINE) &&
(point <= syncobj->timeline)) {
struct drm_syncobj_stub_fence *fence =
kzalloc(sizeof(struct drm_syncobj_stub_fence),
GFP_KERNEL);
if (!fence)
return NULL;
spin_lock_init(&fence->lock);
dma_fence_init(&fence->base,
&drm_syncobj_stub_fence_ops,
&fence->lock,
syncobj->timeline_context,
point);
dma_fence_signal(&fence->base);
return &fence->base;
}
list_for_each_entry(signal_pt, &syncobj->signal_pt_list, list) {
if (point > signal_pt->value)
continue;
if ((syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) &&
(point != signal_pt->value))
continue;
return dma_fence_get(&signal_pt->fence_array->base);
}
return NULL;
}
static void drm_syncobj_add_callback_locked(struct drm_syncobj *syncobj,
struct drm_syncobj_cb *cb,
drm_syncobj_func_t func)
{
cb->func = func;
list_add_tail(&cb->node, &syncobj->cb_list);
}
static void drm_syncobj_fence_get_or_add_callback(struct drm_syncobj *syncobj,
struct dma_fence **fence,
struct drm_syncobj_cb *cb,
drm_syncobj_func_t func)
{
u64 pt_value =