]>
Commit | Line | Data |
---|---|---|
1867a23b GP |
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 | }; |