]>
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> | |
aff9da10 | 20 | #include <linux/slab.h> |
1867a23b GP |
21 | #include <linux/sync_file.h> |
22 | ||
1fe82e2e | 23 | #include "sync_debug.h" |
1867a23b | 24 | |
aff9da10 GP |
25 | #define CREATE_TRACE_POINTS |
26 | #include "trace/sync.h" | |
27 | ||
6f65aa89 GP |
28 | struct sw_sync_create_fence_data { |
29 | __u32 value; | |
30 | char name[32]; | |
31 | __s32 fence; /* fd of new fence */ | |
32 | }; | |
33 | ||
34 | #define SW_SYNC_IOC_MAGIC 'W' | |
35 | ||
36 | #define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0,\ | |
37 | struct sw_sync_create_fence_data) | |
38 | #define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32) | |
39 | ||
aff9da10 GP |
40 | static const struct fence_ops timeline_fence_ops; |
41 | ||
42 | static inline struct sync_pt *fence_to_sync_pt(struct fence *fence) | |
43 | { | |
44 | if (fence->ops != &timeline_fence_ops) | |
45 | return NULL; | |
46 | return container_of(fence, struct sync_pt, base); | |
47 | } | |
48 | ||
49 | /** | |
50 | * sync_timeline_create() - creates a sync object | |
aff9da10 GP |
51 | * @name: sync_timeline name |
52 | * | |
53 | * Creates a new sync_timeline. Returns the sync_timeline object or NULL in | |
54 | * case of error. | |
55 | */ | |
b9bc2b7b | 56 | struct sync_timeline *sync_timeline_create(const char *name) |
aff9da10 GP |
57 | { |
58 | struct sync_timeline *obj; | |
59 | ||
60 | obj = kzalloc(sizeof(*obj), GFP_KERNEL); | |
61 | if (!obj) | |
62 | return NULL; | |
63 | ||
64 | kref_init(&obj->kref); | |
65 | obj->context = fence_context_alloc(1); | |
66 | strlcpy(obj->name, name, sizeof(obj->name)); | |
aff9da10 GP |
67 | |
68 | INIT_LIST_HEAD(&obj->child_list_head); | |
69 | INIT_LIST_HEAD(&obj->active_list_head); | |
70 | spin_lock_init(&obj->child_list_lock); | |
71 | ||
72 | sync_timeline_debug_add(obj); | |
73 | ||
74 | return obj; | |
75 | } | |
76 | ||
77 | static void sync_timeline_free(struct kref *kref) | |
78 | { | |
79 | struct sync_timeline *obj = | |
80 | container_of(kref, struct sync_timeline, kref); | |
81 | ||
82 | sync_timeline_debug_remove(obj); | |
83 | ||
84 | kfree(obj); | |
85 | } | |
86 | ||
87 | static void sync_timeline_get(struct sync_timeline *obj) | |
88 | { | |
89 | kref_get(&obj->kref); | |
90 | } | |
91 | ||
92 | static void sync_timeline_put(struct sync_timeline *obj) | |
93 | { | |
94 | kref_put(&obj->kref, sync_timeline_free); | |
95 | } | |
96 | ||
aff9da10 GP |
97 | /** |
98 | * sync_timeline_signal() - signal a status change on a sync_timeline | |
99 | * @obj: sync_timeline to signal | |
100 | * @inc: num to increment on timeline->value | |
101 | * | |
102 | * A sync implementation should call this any time one of it's fences | |
103 | * has signaled or has an error condition. | |
104 | */ | |
105 | static void sync_timeline_signal(struct sync_timeline *obj, unsigned int inc) | |
106 | { | |
107 | unsigned long flags; | |
108 | struct sync_pt *pt, *next; | |
109 | ||
110 | trace_sync_timeline(obj); | |
111 | ||
112 | spin_lock_irqsave(&obj->child_list_lock, flags); | |
113 | ||
114 | obj->value += inc; | |
115 | ||
116 | list_for_each_entry_safe(pt, next, &obj->active_list_head, | |
117 | active_list) { | |
118 | if (fence_is_signaled_locked(&pt->base)) | |
119 | list_del_init(&pt->active_list); | |
120 | } | |
121 | ||
122 | spin_unlock_irqrestore(&obj->child_list_lock, flags); | |
123 | } | |
124 | ||
125 | /** | |
126 | * sync_pt_create() - creates a sync pt | |
127 | * @parent: fence's parent sync_timeline | |
128 | * @size: size to allocate for this pt | |
129 | * @inc: value of the fence | |
130 | * | |
131 | * Creates a new sync_pt as a child of @parent. @size bytes will be | |
132 | * allocated allowing for implementation specific data to be kept after | |
133 | * the generic sync_timeline struct. Returns the sync_pt object or | |
134 | * NULL in case of error. | |
135 | */ | |
136 | static struct sync_pt *sync_pt_create(struct sync_timeline *obj, int size, | |
137 | unsigned int value) | |
138 | { | |
139 | unsigned long flags; | |
140 | struct sync_pt *pt; | |
141 | ||
142 | if (size < sizeof(*pt)) | |
143 | return NULL; | |
144 | ||
145 | pt = kzalloc(size, GFP_KERNEL); | |
146 | if (!pt) | |
147 | return NULL; | |
148 | ||
149 | spin_lock_irqsave(&obj->child_list_lock, flags); | |
150 | sync_timeline_get(obj); | |
151 | fence_init(&pt->base, &timeline_fence_ops, &obj->child_list_lock, | |
152 | obj->context, value); | |
153 | list_add_tail(&pt->child_list, &obj->child_list_head); | |
154 | INIT_LIST_HEAD(&pt->active_list); | |
155 | spin_unlock_irqrestore(&obj->child_list_lock, flags); | |
156 | return pt; | |
157 | } | |
158 | ||
159 | static const char *timeline_fence_get_driver_name(struct fence *fence) | |
160 | { | |
b9bc2b7b | 161 | return "sw_sync"; |
aff9da10 GP |
162 | } |
163 | ||
164 | static const char *timeline_fence_get_timeline_name(struct fence *fence) | |
165 | { | |
166 | struct sync_timeline *parent = fence_parent(fence); | |
167 | ||
168 | return parent->name; | |
169 | } | |
170 | ||
171 | static void timeline_fence_release(struct fence *fence) | |
172 | { | |
173 | struct sync_pt *pt = fence_to_sync_pt(fence); | |
174 | struct sync_timeline *parent = fence_parent(fence); | |
175 | unsigned long flags; | |
176 | ||
177 | spin_lock_irqsave(fence->lock, flags); | |
178 | list_del(&pt->child_list); | |
a4ebee65 | 179 | if (!list_empty(&pt->active_list)) |
aff9da10 GP |
180 | list_del(&pt->active_list); |
181 | spin_unlock_irqrestore(fence->lock, flags); | |
182 | ||
183 | sync_timeline_put(parent); | |
184 | fence_free(fence); | |
185 | } | |
186 | ||
187 | static bool timeline_fence_signaled(struct fence *fence) | |
188 | { | |
189 | struct sync_timeline *parent = fence_parent(fence); | |
190 | ||
191 | return (fence->seqno > parent->value) ? false : true; | |
192 | } | |
193 | ||
194 | static bool timeline_fence_enable_signaling(struct fence *fence) | |
195 | { | |
196 | struct sync_pt *pt = fence_to_sync_pt(fence); | |
197 | struct sync_timeline *parent = fence_parent(fence); | |
198 | ||
199 | if (timeline_fence_signaled(fence)) | |
200 | return false; | |
201 | ||
202 | list_add_tail(&pt->active_list, &parent->active_list_head); | |
203 | return true; | |
204 | } | |
205 | ||
206 | static void timeline_fence_value_str(struct fence *fence, | |
207 | char *str, int size) | |
208 | { | |
209 | snprintf(str, size, "%d", fence->seqno); | |
210 | } | |
211 | ||
212 | static void timeline_fence_timeline_value_str(struct fence *fence, | |
213 | char *str, int size) | |
214 | { | |
215 | struct sync_timeline *parent = fence_parent(fence); | |
216 | ||
217 | snprintf(str, size, "%d", parent->value); | |
218 | } | |
219 | ||
220 | static const struct fence_ops timeline_fence_ops = { | |
221 | .get_driver_name = timeline_fence_get_driver_name, | |
222 | .get_timeline_name = timeline_fence_get_timeline_name, | |
223 | .enable_signaling = timeline_fence_enable_signaling, | |
224 | .signaled = timeline_fence_signaled, | |
225 | .wait = fence_default_wait, | |
226 | .release = timeline_fence_release, | |
227 | .fence_value_str = timeline_fence_value_str, | |
228 | .timeline_value_str = timeline_fence_timeline_value_str, | |
229 | }; | |
230 | ||
1867a23b GP |
231 | /* |
232 | * *WARNING* | |
233 | * | |
234 | * improper use of this can result in deadlocking kernel drivers from userspace. | |
235 | */ | |
236 | ||
237 | /* opening sw_sync create a new sync obj */ | |
238 | static int sw_sync_debugfs_open(struct inode *inode, struct file *file) | |
239 | { | |
240 | struct sync_timeline *obj; | |
241 | char task_comm[TASK_COMM_LEN]; | |
242 | ||
243 | get_task_comm(task_comm, current); | |
244 | ||
b9bc2b7b | 245 | obj = sync_timeline_create(task_comm); |
1867a23b GP |
246 | if (!obj) |
247 | return -ENOMEM; | |
248 | ||
249 | file->private_data = obj; | |
250 | ||
251 | return 0; | |
252 | } | |
253 | ||
254 | static int sw_sync_debugfs_release(struct inode *inode, struct file *file) | |
255 | { | |
256 | struct sync_timeline *obj = file->private_data; | |
257 | ||
71110232 GP |
258 | smp_wmb(); |
259 | ||
260 | sync_timeline_put(obj); | |
1867a23b GP |
261 | return 0; |
262 | } | |
263 | ||
264 | static long sw_sync_ioctl_create_fence(struct sync_timeline *obj, | |
265 | unsigned long arg) | |
266 | { | |
267 | int fd = get_unused_fd_flags(O_CLOEXEC); | |
268 | int err; | |
269 | struct sync_pt *pt; | |
270 | struct sync_file *sync_file; | |
271 | struct sw_sync_create_fence_data data; | |
272 | ||
273 | if (fd < 0) | |
274 | return fd; | |
275 | ||
276 | if (copy_from_user(&data, (void __user *)arg, sizeof(data))) { | |
277 | err = -EFAULT; | |
278 | goto err; | |
279 | } | |
280 | ||
281 | pt = sync_pt_create(obj, sizeof(*pt), data.value); | |
282 | if (!pt) { | |
283 | err = -ENOMEM; | |
284 | goto err; | |
285 | } | |
286 | ||
287 | sync_file = sync_file_create(&pt->base); | |
288 | if (!sync_file) { | |
289 | fence_put(&pt->base); | |
290 | err = -ENOMEM; | |
291 | goto err; | |
292 | } | |
293 | ||
294 | data.fence = fd; | |
295 | if (copy_to_user((void __user *)arg, &data, sizeof(data))) { | |
296 | fput(sync_file->file); | |
297 | err = -EFAULT; | |
298 | goto err; | |
299 | } | |
300 | ||
301 | fd_install(fd, sync_file->file); | |
302 | ||
303 | return 0; | |
304 | ||
305 | err: | |
306 | put_unused_fd(fd); | |
307 | return err; | |
308 | } | |
309 | ||
310 | static long sw_sync_ioctl_inc(struct sync_timeline *obj, unsigned long arg) | |
311 | { | |
312 | u32 value; | |
313 | ||
314 | if (copy_from_user(&value, (void __user *)arg, sizeof(value))) | |
315 | return -EFAULT; | |
316 | ||
317 | sync_timeline_signal(obj, value); | |
318 | ||
319 | return 0; | |
320 | } | |
321 | ||
322 | static long sw_sync_ioctl(struct file *file, unsigned int cmd, | |
323 | unsigned long arg) | |
324 | { | |
325 | struct sync_timeline *obj = file->private_data; | |
326 | ||
327 | switch (cmd) { | |
328 | case SW_SYNC_IOC_CREATE_FENCE: | |
329 | return sw_sync_ioctl_create_fence(obj, arg); | |
330 | ||
331 | case SW_SYNC_IOC_INC: | |
332 | return sw_sync_ioctl_inc(obj, arg); | |
333 | ||
334 | default: | |
335 | return -ENOTTY; | |
336 | } | |
337 | } | |
338 | ||
339 | const struct file_operations sw_sync_debugfs_fops = { | |
340 | .open = sw_sync_debugfs_open, | |
341 | .release = sw_sync_debugfs_release, | |
342 | .unlocked_ioctl = sw_sync_ioctl, | |
343 | .compat_ioctl = sw_sync_ioctl, | |
344 | }; |