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