brintos

brintos / linux-shallow public Read only

0
0
Text · 8.8 KiB · d9b1c1b Raw
392 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * drivers/dma-buf/sync_file.c4 *5 * Copyright (C) 2012 Google, Inc.6 */7 8#include <linux/dma-fence-unwrap.h>9#include <linux/export.h>10#include <linux/file.h>11#include <linux/fs.h>12#include <linux/kernel.h>13#include <linux/poll.h>14#include <linux/sched.h>15#include <linux/slab.h>16#include <linux/uaccess.h>17#include <linux/anon_inodes.h>18#include <linux/sync_file.h>19#include <uapi/linux/sync_file.h>20 21static const struct file_operations sync_file_fops;22 23static struct sync_file *sync_file_alloc(void)24{25	struct sync_file *sync_file;26 27	sync_file = kzalloc(sizeof(*sync_file), GFP_KERNEL);28	if (!sync_file)29		return NULL;30 31	sync_file->file = anon_inode_getfile("sync_file", &sync_file_fops,32					     sync_file, 0);33	if (IS_ERR(sync_file->file))34		goto err;35 36	init_waitqueue_head(&sync_file->wq);37 38	INIT_LIST_HEAD(&sync_file->cb.node);39 40	return sync_file;41 42err:43	kfree(sync_file);44	return NULL;45}46 47static void fence_check_cb_func(struct dma_fence *f, struct dma_fence_cb *cb)48{49	struct sync_file *sync_file;50 51	sync_file = container_of(cb, struct sync_file, cb);52 53	wake_up_all(&sync_file->wq);54}55 56/**57 * sync_file_create() - creates a sync file58 * @fence:	fence to add to the sync_fence59 *60 * Creates a sync_file containg @fence. This function acquires and additional61 * reference of @fence for the newly-created &sync_file, if it succeeds. The62 * sync_file can be released with fput(sync_file->file). Returns the63 * sync_file or NULL in case of error.64 */65struct sync_file *sync_file_create(struct dma_fence *fence)66{67	struct sync_file *sync_file;68 69	sync_file = sync_file_alloc();70	if (!sync_file)71		return NULL;72 73	sync_file->fence = dma_fence_get(fence);74 75	return sync_file;76}77EXPORT_SYMBOL(sync_file_create);78 79static struct sync_file *sync_file_fdget(int fd)80{81	struct file *file = fget(fd);82 83	if (!file)84		return NULL;85 86	if (file->f_op != &sync_file_fops)87		goto err;88 89	return file->private_data;90 91err:92	fput(file);93	return NULL;94}95 96/**97 * sync_file_get_fence - get the fence related to the sync_file fd98 * @fd:		sync_file fd to get the fence from99 *100 * Ensures @fd references a valid sync_file and returns a fence that101 * represents all fence in the sync_file. On error NULL is returned.102 */103struct dma_fence *sync_file_get_fence(int fd)104{105	struct sync_file *sync_file;106	struct dma_fence *fence;107 108	sync_file = sync_file_fdget(fd);109	if (!sync_file)110		return NULL;111 112	fence = dma_fence_get(sync_file->fence);113	fput(sync_file->file);114 115	return fence;116}117EXPORT_SYMBOL(sync_file_get_fence);118 119/**120 * sync_file_get_name - get the name of the sync_file121 * @sync_file:		sync_file to get the fence from122 * @buf:		destination buffer to copy sync_file name into123 * @len:		available size of destination buffer.124 *125 * Each sync_file may have a name assigned either by the user (when merging126 * sync_files together) or created from the fence it contains. In the latter127 * case construction of the name is deferred until use, and so requires128 * sync_file_get_name().129 *130 * Returns: a string representing the name.131 */132char *sync_file_get_name(struct sync_file *sync_file, char *buf, int len)133{134	if (sync_file->user_name[0]) {135		strscpy(buf, sync_file->user_name, len);136	} else {137		struct dma_fence *fence = sync_file->fence;138 139		snprintf(buf, len, "%s-%s%llu-%lld",140			 fence->ops->get_driver_name(fence),141			 fence->ops->get_timeline_name(fence),142			 fence->context,143			 fence->seqno);144	}145 146	return buf;147}148 149/**150 * sync_file_merge() - merge two sync_files151 * @name:	name of new fence152 * @a:		sync_file a153 * @b:		sync_file b154 *155 * Creates a new sync_file which contains copies of all the fences in both156 * @a and @b.  @a and @b remain valid, independent sync_file. Returns the157 * new merged sync_file or NULL in case of error.158 */159static struct sync_file *sync_file_merge(const char *name, struct sync_file *a,160					 struct sync_file *b)161{162	struct sync_file *sync_file;163	struct dma_fence *fence;164 165	sync_file = sync_file_alloc();166	if (!sync_file)167		return NULL;168 169	fence = dma_fence_unwrap_merge(a->fence, b->fence);170	if (!fence) {171		fput(sync_file->file);172		return NULL;173	}174	sync_file->fence = fence;175	strscpy(sync_file->user_name, name, sizeof(sync_file->user_name));176	return sync_file;177}178 179static int sync_file_release(struct inode *inode, struct file *file)180{181	struct sync_file *sync_file = file->private_data;182 183	if (test_bit(POLL_ENABLED, &sync_file->flags))184		dma_fence_remove_callback(sync_file->fence, &sync_file->cb);185	dma_fence_put(sync_file->fence);186	kfree(sync_file);187 188	return 0;189}190 191static __poll_t sync_file_poll(struct file *file, poll_table *wait)192{193	struct sync_file *sync_file = file->private_data;194 195	poll_wait(file, &sync_file->wq, wait);196 197	if (list_empty(&sync_file->cb.node) &&198	    !test_and_set_bit(POLL_ENABLED, &sync_file->flags)) {199		if (dma_fence_add_callback(sync_file->fence, &sync_file->cb,200					   fence_check_cb_func) < 0)201			wake_up_all(&sync_file->wq);202	}203 204	return dma_fence_is_signaled(sync_file->fence) ? EPOLLIN : 0;205}206 207static long sync_file_ioctl_merge(struct sync_file *sync_file,208				  unsigned long arg)209{210	int fd = get_unused_fd_flags(O_CLOEXEC);211	int err;212	struct sync_file *fence2, *fence3;213	struct sync_merge_data data;214 215	if (fd < 0)216		return fd;217 218	if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {219		err = -EFAULT;220		goto err_put_fd;221	}222 223	if (data.flags || data.pad) {224		err = -EINVAL;225		goto err_put_fd;226	}227 228	fence2 = sync_file_fdget(data.fd2);229	if (!fence2) {230		err = -ENOENT;231		goto err_put_fd;232	}233 234	data.name[sizeof(data.name) - 1] = '\0';235	fence3 = sync_file_merge(data.name, sync_file, fence2);236	if (!fence3) {237		err = -ENOMEM;238		goto err_put_fence2;239	}240 241	data.fence = fd;242	if (copy_to_user((void __user *)arg, &data, sizeof(data))) {243		err = -EFAULT;244		goto err_put_fence3;245	}246 247	fd_install(fd, fence3->file);248	fput(fence2->file);249	return 0;250 251err_put_fence3:252	fput(fence3->file);253 254err_put_fence2:255	fput(fence2->file);256 257err_put_fd:258	put_unused_fd(fd);259	return err;260}261 262static int sync_fill_fence_info(struct dma_fence *fence,263				 struct sync_fence_info *info)264{265	strscpy(info->obj_name, fence->ops->get_timeline_name(fence),266		sizeof(info->obj_name));267	strscpy(info->driver_name, fence->ops->get_driver_name(fence),268		sizeof(info->driver_name));269 270	info->status = dma_fence_get_status(fence);271	info->timestamp_ns =272		dma_fence_is_signaled(fence) ?273			ktime_to_ns(dma_fence_timestamp(fence)) :274			ktime_set(0, 0);275 276	return info->status;277}278 279static long sync_file_ioctl_fence_info(struct sync_file *sync_file,280				       unsigned long arg)281{282	struct sync_fence_info *fence_info = NULL;283	struct dma_fence_unwrap iter;284	struct sync_file_info info;285	unsigned int num_fences;286	struct dma_fence *fence;287	int ret;288	__u32 size;289 290	if (copy_from_user(&info, (void __user *)arg, sizeof(info)))291		return -EFAULT;292 293	if (info.flags || info.pad)294		return -EINVAL;295 296	num_fences = 0;297	dma_fence_unwrap_for_each(fence, &iter, sync_file->fence)298		++num_fences;299 300	/*301	 * Passing num_fences = 0 means that userspace doesn't want to302	 * retrieve any sync_fence_info. If num_fences = 0 we skip filling303	 * sync_fence_info and return the actual number of fences on304	 * info->num_fences.305	 */306	if (!info.num_fences) {307		info.status = dma_fence_get_status(sync_file->fence);308		goto no_fences;309	} else {310		info.status = 1;311	}312 313	if (info.num_fences < num_fences)314		return -EINVAL;315 316	size = num_fences * sizeof(*fence_info);317	fence_info = kzalloc(size, GFP_KERNEL);318	if (!fence_info)319		return -ENOMEM;320 321	num_fences = 0;322	dma_fence_unwrap_for_each(fence, &iter, sync_file->fence) {323		int status;324 325		status = sync_fill_fence_info(fence, &fence_info[num_fences++]);326		info.status = info.status <= 0 ? info.status : status;327	}328 329	if (copy_to_user(u64_to_user_ptr(info.sync_fence_info), fence_info,330			 size)) {331		ret = -EFAULT;332		goto out;333	}334 335no_fences:336	sync_file_get_name(sync_file, info.name, sizeof(info.name));337	info.num_fences = num_fences;338 339	if (copy_to_user((void __user *)arg, &info, sizeof(info)))340		ret = -EFAULT;341	else342		ret = 0;343 344out:345	kfree(fence_info);346 347	return ret;348}349 350static int sync_file_ioctl_set_deadline(struct sync_file *sync_file,351					unsigned long arg)352{353	struct sync_set_deadline ts;354 355	if (copy_from_user(&ts, (void __user *)arg, sizeof(ts)))356		return -EFAULT;357 358	if (ts.pad)359		return -EINVAL;360 361	dma_fence_set_deadline(sync_file->fence, ns_to_ktime(ts.deadline_ns));362 363	return 0;364}365 366static long sync_file_ioctl(struct file *file, unsigned int cmd,367			    unsigned long arg)368{369	struct sync_file *sync_file = file->private_data;370 371	switch (cmd) {372	case SYNC_IOC_MERGE:373		return sync_file_ioctl_merge(sync_file, arg);374 375	case SYNC_IOC_FILE_INFO:376		return sync_file_ioctl_fence_info(sync_file, arg);377 378	case SYNC_IOC_SET_DEADLINE:379		return sync_file_ioctl_set_deadline(sync_file, arg);380 381	default:382		return -ENOTTY;383	}384}385 386static const struct file_operations sync_file_fops = {387	.release = sync_file_release,388	.poll = sync_file_poll,389	.unlocked_ioctl = sync_file_ioctl,390	.compat_ioctl = compat_ptr_ioctl,391};392