962 lines · 23.3 KB · C
RawDownload
/* * 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> /** * 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 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 int 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){	int ret; 	*fence = drm_syncobj_fence_get(syncobj);	if (*fence)		return 1; 	spin_lock(&syncobj->lock);	/* We've already tried once to get a fence and failed.  Now that we	 * have the lock, try one more time just to be sure we don't add a	 * callback when a fence has already been set.	 */	if (syncobj->fence) {		*fence = dma_fence_get(rcu_dereference_protected(syncobj->fence,								 lockdep_is_held(&syncobj->lock)));		ret = 1;	} else {		*fence = NULL;		drm_syncobj_add_callback_locked(syncobj, cb, func);		ret = 0;	}	spin_unlock(&syncobj->lock); 	return ret;} void drm_syncobj_add_callback(struct drm_syncobj *syncobj,			      struct drm_syncobj_cb *cb,			      drm_syncobj_func_t func){	spin_lock(&syncobj->lock);	drm_syncobj_add_callback_locked(syncobj, cb, func);	spin_unlock(&syncobj->lock);} void drm_syncobj_remove_callback(struct drm_syncobj *syncobj,				 struct drm_syncobj_cb *cb){	spin_lock(&syncobj->lock);	list_del_init(&cb->node);	spin_unlock(&syncobj->lock);} /** * drm_syncobj_replace_fence - replace fence in a sync object. * @syncobj: Sync object to replace fence in * @fence: fence to install in sync file. * * This replaces the fence on a sync object. */void drm_syncobj_replace_fence(struct drm_syncobj *syncobj,			       struct dma_fence *fence){	struct dma_fence *old_fence;	struct drm_syncobj_cb *cur, *tmp; 	if (fence)		dma_fence_get(fence); 	spin_lock(&syncobj->lock); 	old_fence = rcu_dereference_protected(syncobj->fence,					      lockdep_is_held(&syncobj->lock));	rcu_assign_pointer(syncobj->fence, fence); 	if (fence != old_fence) {		list_for_each_entry_safe(cur, tmp, &syncobj->cb_list, node) {			list_del_init(&cur->node);			cur->func(syncobj, cur);		}	} 	spin_unlock(&syncobj->lock); 	dma_fence_put(old_fence);}EXPORT_SYMBOL(drm_syncobj_replace_fence); /** * drm_syncobj_assign_null_handle - assign a stub fence to the sync object * @syncobj: sync object to assign the fence on * * Assign a already signaled stub fence to the sync object. */static void drm_syncobj_assign_null_handle(struct drm_syncobj *syncobj){	struct dma_fence *fence = dma_fence_get_stub(); 	drm_syncobj_replace_fence(syncobj, fence);	dma_fence_put(fence);} /** * drm_syncobj_find_fence - lookup and reference the fence in a sync object * @file_private: drm file private pointer * @handle: sync object handle to lookup. * @point: timeline point * @flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or not * @fence: out parameter for the fence * * This is just a convenience function that combines drm_syncobj_find() and * drm_syncobj_fence_get(). * * Returns 0 on success or a negative error value on failure. On success @fence * contains a reference to the fence, which must be released by calling * dma_fence_put(). */int drm_syncobj_find_fence(struct drm_file *file_private,			   u32 handle, u64 point, u64 flags,			   struct dma_fence **fence){	struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);	int ret = 0; 	if (!syncobj)		return -ENOENT; 	*fence = drm_syncobj_fence_get(syncobj);	if (!*fence) {		ret = -EINVAL;	}	drm_syncobj_put(syncobj);	return ret;}EXPORT_SYMBOL(drm_syncobj_find_fence); /** * drm_syncobj_free - free a sync object. * @kref: kref to free. * * Only to be called from kref_put in drm_syncobj_put. */void drm_syncobj_free(struct kref *kref){	struct drm_syncobj *syncobj = container_of(kref,						   struct drm_syncobj,						   refcount);	drm_syncobj_replace_fence(syncobj, NULL);	kfree(syncobj);}EXPORT_SYMBOL(drm_syncobj_free); /** * drm_syncobj_create - create a new syncobj * @out_syncobj: returned syncobj * @flags: DRM_SYNCOBJ_* flags * @fence: if non-NULL, the syncobj will represent this fence * * This is the first function to create a sync object. After creating, drivers * probably want to make it available to userspace, either through * drm_syncobj_get_handle() or drm_syncobj_get_fd(). * * Returns 0 on success or a negative error value on failure. */int drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags,		       struct dma_fence *fence){	struct drm_syncobj *syncobj; 	syncobj = kzalloc(sizeof(struct drm_syncobj), GFP_KERNEL);	if (!syncobj)		return -ENOMEM; 	kref_init(&syncobj->refcount);	INIT_LIST_HEAD(&syncobj->cb_list);	spin_lock_init(&syncobj->lock); 	if (flags & DRM_SYNCOBJ_CREATE_SIGNALED)		drm_syncobj_assign_null_handle(syncobj); 	if (fence)		drm_syncobj_replace_fence(syncobj, fence); 	*out_syncobj = syncobj;	return 0;}EXPORT_SYMBOL(drm_syncobj_create); /** * drm_syncobj_get_handle - get a handle from a syncobj * @file_private: drm file private pointer * @syncobj: Sync object to export * @handle: out parameter with the new handle * * Exports a sync object created with drm_syncobj_create() as a handle on * @file_private to userspace. * * Returns 0 on success or a negative error value on failure. */int drm_syncobj_get_handle(struct drm_file *file_private,			   struct drm_syncobj *syncobj, u32 *handle){	int ret; 	/* take a reference to put in the idr */	drm_syncobj_get(syncobj); 	idr_preload(GFP_KERNEL);	spin_lock(&file_private->syncobj_table_lock);	ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT);	spin_unlock(&file_private->syncobj_table_lock); 	idr_preload_end(); 	if (ret < 0) {		drm_syncobj_put(syncobj);		return ret;	} 	*handle = ret;	return 0;}EXPORT_SYMBOL(drm_syncobj_get_handle); static int drm_syncobj_create_as_handle(struct drm_file *file_private,					u32 *handle, uint32_t flags){	int ret;	struct drm_syncobj *syncobj; 	ret = drm_syncobj_create(&syncobj, flags, NULL);	if (ret)		return ret; 	ret = drm_syncobj_get_handle(file_private, syncobj, handle);	drm_syncobj_put(syncobj);	return ret;} static int drm_syncobj_destroy(struct drm_file *file_private,			       u32 handle){	struct drm_syncobj *syncobj; 	spin_lock(&file_private->syncobj_table_lock);	syncobj = idr_remove(&file_private->syncobj_idr, handle);	spin_unlock(&file_private->syncobj_table_lock); 	if (!syncobj)		return -EINVAL; 	drm_syncobj_put(syncobj);	return 0;} static int drm_syncobj_file_release(struct inode *inode, struct file *file){	struct drm_syncobj *syncobj = file->private_data; 	drm_syncobj_put(syncobj);	return 0;} static const struct file_operations drm_syncobj_file_fops = {	.release = drm_syncobj_file_release,}; /** * drm_syncobj_get_fd - get a file descriptor from a syncobj * @syncobj: Sync object to export * @p_fd: out parameter with the new file descriptor * * Exports a sync object created with drm_syncobj_create() as a file descriptor. * * Returns 0 on success or a negative error value on failure. */int drm_syncobj_get_fd(struct drm_syncobj *syncobj, int *p_fd){	struct file *file;	int fd; 	fd = get_unused_fd_flags(O_CLOEXEC);	if (fd < 0)		return fd; 	file = anon_inode_getfile("syncobj_file",				  &drm_syncobj_file_fops,				  syncobj, 0);	if (IS_ERR(file)) {		put_unused_fd(fd);		return PTR_ERR(file);	} 	drm_syncobj_get(syncobj);	fd_install(fd, file); 	*p_fd = fd;	return 0;}EXPORT_SYMBOL(drm_syncobj_get_fd); static int drm_syncobj_handle_to_fd(struct drm_file *file_private,				    u32 handle, int *p_fd){	struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);	int ret; 	if (!syncobj)		return -EINVAL; 	ret = drm_syncobj_get_fd(syncobj, p_fd);	drm_syncobj_put(syncobj);	return ret;} static int drm_syncobj_fd_to_handle(struct drm_file *file_private,				    int fd, u32 *handle){	struct drm_syncobj *syncobj;	struct file *file;	int ret; 	file = fget(fd);	if (!file)		return -EINVAL; 	if (file->f_op != &drm_syncobj_file_fops) {		fput(file);		return -EINVAL;	} 	/* take a reference to put in the idr */	syncobj = file->private_data;	drm_syncobj_get(syncobj); 	idr_preload(GFP_KERNEL);	spin_lock(&file_private->syncobj_table_lock);	ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT);	spin_unlock(&file_private->syncobj_table_lock);	idr_preload_end(); 	if (ret > 0) {		*handle = ret;		ret = 0;	} else		drm_syncobj_put(syncobj); 	fput(file);	return ret;} static int drm_syncobj_import_sync_file_fence(struct drm_file *file_private,					      int fd, int handle){	struct dma_fence *fence = sync_file_get_fence(fd);	struct drm_syncobj *syncobj; 	if (!fence)		return -EINVAL; 	syncobj = drm_syncobj_find(file_private, handle);	if (!syncobj) {		dma_fence_put(fence);		return -ENOENT;	} 	drm_syncobj_replace_fence(syncobj, fence);	dma_fence_put(fence);	drm_syncobj_put(syncobj);	return 0;} static int drm_syncobj_export_sync_file(struct drm_file *file_private,					int handle, int *p_fd){	int ret;	struct dma_fence *fence;	struct sync_file *sync_file;	int fd = get_unused_fd_flags(O_CLOEXEC); 	if (fd < 0)		return fd; 	ret = drm_syncobj_find_fence(file_private, handle, 0, 0, &fence);	if (ret)		goto err_put_fd; 	sync_file = sync_file_create(fence); 	dma_fence_put(fence); 	if (!sync_file) {		ret = -EINVAL;		goto err_put_fd;	} 	fd_install(fd, sync_file->file); 	*p_fd = fd;	return 0;err_put_fd:	put_unused_fd(fd);	return ret;}/** * drm_syncobj_open - initalizes syncobj file-private structures at devnode open time * @file_private: drm file-private structure to set up * * Called at device open time, sets up the structure for handling refcounting * of sync objects. */voiddrm_syncobj_open(struct drm_file *file_private){

Lines 1–500 of 962.