Gustavo Padovan | 1867a23 | 2016-05-31 16:59:05 -0300 | [diff] [blame^] | 1 | /* |
| 2 | * drivers/dma-buf/sw_sync.c |
| 3 | * |
| 4 | * Copyright (C) 2012 Google, Inc. |
| 5 | * |
| 6 | * This software is licensed under the terms of the GNU General Public |
| 7 | * License version 2, as published by the Free Software Foundation, and |
| 8 | * may be copied, distributed, and modified under those terms. |
| 9 | * |
| 10 | * This program is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | * GNU General Public License for more details. |
| 14 | * |
| 15 | */ |
| 16 | |
| 17 | #include <linux/file.h> |
| 18 | #include <linux/fs.h> |
| 19 | #include <linux/uaccess.h> |
| 20 | #include <linux/sync_file.h> |
| 21 | |
| 22 | #include "uapi/sw_sync.h" |
| 23 | #include "sync.h" |
| 24 | |
| 25 | /* |
| 26 | * *WARNING* |
| 27 | * |
| 28 | * improper use of this can result in deadlocking kernel drivers from userspace. |
| 29 | */ |
| 30 | |
| 31 | /* opening sw_sync create a new sync obj */ |
| 32 | static int sw_sync_debugfs_open(struct inode *inode, struct file *file) |
| 33 | { |
| 34 | struct sync_timeline *obj; |
| 35 | char task_comm[TASK_COMM_LEN]; |
| 36 | |
| 37 | get_task_comm(task_comm, current); |
| 38 | |
| 39 | obj = sync_timeline_create("sw_sync", task_comm); |
| 40 | if (!obj) |
| 41 | return -ENOMEM; |
| 42 | |
| 43 | file->private_data = obj; |
| 44 | |
| 45 | return 0; |
| 46 | } |
| 47 | |
| 48 | static int sw_sync_debugfs_release(struct inode *inode, struct file *file) |
| 49 | { |
| 50 | struct sync_timeline *obj = file->private_data; |
| 51 | |
| 52 | sync_timeline_destroy(obj); |
| 53 | return 0; |
| 54 | } |
| 55 | |
| 56 | static long sw_sync_ioctl_create_fence(struct sync_timeline *obj, |
| 57 | unsigned long arg) |
| 58 | { |
| 59 | int fd = get_unused_fd_flags(O_CLOEXEC); |
| 60 | int err; |
| 61 | struct sync_pt *pt; |
| 62 | struct sync_file *sync_file; |
| 63 | struct sw_sync_create_fence_data data; |
| 64 | |
| 65 | if (fd < 0) |
| 66 | return fd; |
| 67 | |
| 68 | if (copy_from_user(&data, (void __user *)arg, sizeof(data))) { |
| 69 | err = -EFAULT; |
| 70 | goto err; |
| 71 | } |
| 72 | |
| 73 | pt = sync_pt_create(obj, sizeof(*pt), data.value); |
| 74 | if (!pt) { |
| 75 | err = -ENOMEM; |
| 76 | goto err; |
| 77 | } |
| 78 | |
| 79 | sync_file = sync_file_create(&pt->base); |
| 80 | if (!sync_file) { |
| 81 | fence_put(&pt->base); |
| 82 | err = -ENOMEM; |
| 83 | goto err; |
| 84 | } |
| 85 | |
| 86 | data.fence = fd; |
| 87 | if (copy_to_user((void __user *)arg, &data, sizeof(data))) { |
| 88 | fput(sync_file->file); |
| 89 | err = -EFAULT; |
| 90 | goto err; |
| 91 | } |
| 92 | |
| 93 | fd_install(fd, sync_file->file); |
| 94 | |
| 95 | return 0; |
| 96 | |
| 97 | err: |
| 98 | put_unused_fd(fd); |
| 99 | return err; |
| 100 | } |
| 101 | |
| 102 | static long sw_sync_ioctl_inc(struct sync_timeline *obj, unsigned long arg) |
| 103 | { |
| 104 | u32 value; |
| 105 | |
| 106 | if (copy_from_user(&value, (void __user *)arg, sizeof(value))) |
| 107 | return -EFAULT; |
| 108 | |
| 109 | sync_timeline_signal(obj, value); |
| 110 | |
| 111 | return 0; |
| 112 | } |
| 113 | |
| 114 | static long sw_sync_ioctl(struct file *file, unsigned int cmd, |
| 115 | unsigned long arg) |
| 116 | { |
| 117 | struct sync_timeline *obj = file->private_data; |
| 118 | |
| 119 | switch (cmd) { |
| 120 | case SW_SYNC_IOC_CREATE_FENCE: |
| 121 | return sw_sync_ioctl_create_fence(obj, arg); |
| 122 | |
| 123 | case SW_SYNC_IOC_INC: |
| 124 | return sw_sync_ioctl_inc(obj, arg); |
| 125 | |
| 126 | default: |
| 127 | return -ENOTTY; |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | const struct file_operations sw_sync_debugfs_fops = { |
| 132 | .open = sw_sync_debugfs_open, |
| 133 | .release = sw_sync_debugfs_release, |
| 134 | .unlocked_ioctl = sw_sync_ioctl, |
| 135 | .compat_ioctl = sw_sync_ioctl, |
| 136 | }; |