]>
Commit | Line | Data |
---|---|---|
1867a23b | 1 | /* |
e912c881 | 2 | * Sync File validation framework |
1867a23b GP |
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 | 25 | #define CREATE_TRACE_POINTS |
a04f915e | 26 | #include "sync_trace.h" |
aff9da10 | 27 | |
fc0c9a03 GP |
28 | /* |
29 | * SW SYNC validation framework | |
30 | * | |
31 | * A sync object driver that uses a 32bit counter to coordinate | |
32 | * synchronization. Useful when there is no hardware primitive backing | |
33 | * the synchronization. | |
34 | * | |
35 | * To start the framework just open: | |
36 | * | |
37 | * <debugfs>/sync/sw_sync | |
38 | * | |
39 | * That will create a sync timeline, all fences created under this timeline | |
40 | * file descriptor will belong to the this timeline. | |
41 | * | |
42 | * The 'sw_sync' file can be opened many times as to create different | |
43 | * timelines. | |
44 | * | |
45 | * Fences can be created with SW_SYNC_IOC_CREATE_FENCE ioctl with struct | |
46 | * sw_sync_ioctl_create_fence as parameter. | |
47 | * | |
48 | * To increment the timeline counter, SW_SYNC_IOC_INC ioctl should be used | |
49 | * with the increment as u32. This will update the last signaled value | |
50 | * from the timeline and signal any fence that has a seqno smaller or equal | |
51 | * to it. | |
52 | * | |
53 | * struct sw_sync_ioctl_create_fence | |
54 | * @value: the seqno to initialise the fence with | |
55 | * @name: the name of the new sync point | |
56 | * @fence: return the fd of the new sync_file with the created fence | |
57 | */ | |
6f65aa89 GP |
58 | struct sw_sync_create_fence_data { |
59 | __u32 value; | |
60 | char name[32]; | |
61 | __s32 fence; /* fd of new fence */ | |
62 | }; | |
63 | ||
64 | #define SW_SYNC_IOC_MAGIC 'W' | |
65 | ||
66 | #define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0,\ | |
67 | struct sw_sync_create_fence_data) | |
fc0c9a03 | 68 | |
6f65aa89 GP |
69 | #define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32) |
70 | ||
f54d1867 | 71 | static const struct dma_fence_ops timeline_fence_ops; |
aff9da10 | 72 | |
f54d1867 | 73 | static inline struct sync_pt *dma_fence_to_sync_pt(struct dma_fence *fence) |
aff9da10 GP |
74 | { |
75 | if (fence->ops != &timeline_fence_ops) | |
76 | return NULL; | |
77 | return container_of(fence, struct sync_pt, base); | |
78 | } | |
79 | ||
80 | /** | |
81 | * sync_timeline_create() - creates a sync object | |
aff9da10 GP |
82 | * @name: sync_timeline name |
83 | * | |
84 | * Creates a new sync_timeline. Returns the sync_timeline object or NULL in | |
85 | * case of error. | |
86 | */ | |
74881588 | 87 | static struct sync_timeline *sync_timeline_create(const char *name) |
aff9da10 GP |
88 | { |
89 | struct sync_timeline *obj; | |
90 | ||
91 | obj = kzalloc(sizeof(*obj), GFP_KERNEL); | |
92 | if (!obj) | |
93 | return NULL; | |
94 | ||
95 | kref_init(&obj->kref); | |
f54d1867 | 96 | obj->context = dma_fence_context_alloc(1); |
aff9da10 | 97 | strlcpy(obj->name, name, sizeof(obj->name)); |
aff9da10 | 98 | |
f1e8c671 | 99 | obj->pt_tree = RB_ROOT; |
d3862e44 CW |
100 | INIT_LIST_HEAD(&obj->pt_list); |
101 | spin_lock_init(&obj->lock); | |
aff9da10 GP |
102 | |
103 | sync_timeline_debug_add(obj); | |
104 | ||
105 | return obj; | |
106 | } | |
107 | ||
108 | static void sync_timeline_free(struct kref *kref) | |
109 | { | |
110 | struct sync_timeline *obj = | |
111 | container_of(kref, struct sync_timeline, kref); | |
112 | ||
113 | sync_timeline_debug_remove(obj); | |
114 | ||
115 | kfree(obj); | |
116 | } | |
117 | ||
118 | static void sync_timeline_get(struct sync_timeline *obj) | |
119 | { | |
120 | kref_get(&obj->kref); | |
121 | } | |
122 | ||
123 | static void sync_timeline_put(struct sync_timeline *obj) | |
124 | { | |
125 | kref_put(&obj->kref, sync_timeline_free); | |
126 | } | |
127 | ||
150b6a9d GP |
128 | static const char *timeline_fence_get_driver_name(struct dma_fence *fence) |
129 | { | |
130 | return "sw_sync"; | |
131 | } | |
132 | ||
133 | static const char *timeline_fence_get_timeline_name(struct dma_fence *fence) | |
134 | { | |
135 | struct sync_timeline *parent = dma_fence_parent(fence); | |
136 | ||
137 | return parent->name; | |
138 | } | |
139 | ||
140 | static void timeline_fence_release(struct dma_fence *fence) | |
141 | { | |
142 | struct sync_pt *pt = dma_fence_to_sync_pt(fence); | |
143 | struct sync_timeline *parent = dma_fence_parent(fence); | |
e66cc217 | 144 | unsigned long flags; |
150b6a9d | 145 | |
e66cc217 | 146 | spin_lock_irqsave(fence->lock, flags); |
150b6a9d | 147 | if (!list_empty(&pt->link)) { |
e66cc217 CW |
148 | list_del(&pt->link); |
149 | rb_erase(&pt->node, &parent->pt_tree); | |
150b6a9d | 150 | } |
e66cc217 | 151 | spin_unlock_irqrestore(fence->lock, flags); |
150b6a9d GP |
152 | |
153 | sync_timeline_put(parent); | |
154 | dma_fence_free(fence); | |
155 | } | |
156 | ||
157 | static bool timeline_fence_signaled(struct dma_fence *fence) | |
158 | { | |
159 | struct sync_timeline *parent = dma_fence_parent(fence); | |
160 | ||
161 | return !__dma_fence_is_later(fence->seqno, parent->value); | |
162 | } | |
163 | ||
164 | static bool timeline_fence_enable_signaling(struct dma_fence *fence) | |
165 | { | |
166 | return true; | |
167 | } | |
168 | ||
169 | static void timeline_fence_value_str(struct dma_fence *fence, | |
170 | char *str, int size) | |
171 | { | |
172 | snprintf(str, size, "%d", fence->seqno); | |
173 | } | |
174 | ||
175 | static void timeline_fence_timeline_value_str(struct dma_fence *fence, | |
176 | char *str, int size) | |
177 | { | |
178 | struct sync_timeline *parent = dma_fence_parent(fence); | |
179 | ||
180 | snprintf(str, size, "%d", parent->value); | |
181 | } | |
182 | ||
183 | static const struct dma_fence_ops timeline_fence_ops = { | |
184 | .get_driver_name = timeline_fence_get_driver_name, | |
185 | .get_timeline_name = timeline_fence_get_timeline_name, | |
186 | .enable_signaling = timeline_fence_enable_signaling, | |
187 | .signaled = timeline_fence_signaled, | |
188 | .wait = dma_fence_default_wait, | |
189 | .release = timeline_fence_release, | |
190 | .fence_value_str = timeline_fence_value_str, | |
191 | .timeline_value_str = timeline_fence_timeline_value_str, | |
192 | }; | |
193 | ||
aff9da10 GP |
194 | /** |
195 | * sync_timeline_signal() - signal a status change on a sync_timeline | |
196 | * @obj: sync_timeline to signal | |
197 | * @inc: num to increment on timeline->value | |
198 | * | |
199 | * A sync implementation should call this any time one of it's fences | |
200 | * has signaled or has an error condition. | |
201 | */ | |
202 | static void sync_timeline_signal(struct sync_timeline *obj, unsigned int inc) | |
203 | { | |
aff9da10 GP |
204 | struct sync_pt *pt, *next; |
205 | ||
206 | trace_sync_timeline(obj); | |
207 | ||
d3862e44 | 208 | spin_lock_irq(&obj->lock); |
aff9da10 GP |
209 | |
210 | obj->value += inc; | |
211 | ||
f1e8c671 | 212 | list_for_each_entry_safe(pt, next, &obj->pt_list, link) { |
3792b7c1 | 213 | if (!timeline_fence_signaled(&pt->base)) |
f1e8c671 CW |
214 | break; |
215 | ||
216 | list_del_init(&pt->link); | |
217 | rb_erase(&pt->node, &obj->pt_tree); | |
3792b7c1 GP |
218 | |
219 | /* | |
220 | * A signal callback may release the last reference to this | |
221 | * fence, causing it to be freed. That operation has to be | |
222 | * last to avoid a use after free inside this loop, and must | |
223 | * be after we remove the fence from the timeline in order to | |
224 | * prevent deadlocking on timeline->lock inside | |
225 | * timeline_fence_release(). | |
226 | */ | |
227 | dma_fence_signal_locked(&pt->base); | |
f1e8c671 | 228 | } |
aff9da10 | 229 | |
d3862e44 | 230 | spin_unlock_irq(&obj->lock); |
aff9da10 GP |
231 | } |
232 | ||
233 | /** | |
234 | * sync_pt_create() - creates a sync pt | |
235 | * @parent: fence's parent sync_timeline | |
aff9da10 GP |
236 | * @inc: value of the fence |
237 | * | |
238 | * Creates a new sync_pt as a child of @parent. @size bytes will be | |
239 | * allocated allowing for implementation specific data to be kept after | |
240 | * the generic sync_timeline struct. Returns the sync_pt object or | |
241 | * NULL in case of error. | |
242 | */ | |
3b52ce44 CW |
243 | static struct sync_pt *sync_pt_create(struct sync_timeline *obj, |
244 | unsigned int value) | |
aff9da10 | 245 | { |
aff9da10 GP |
246 | struct sync_pt *pt; |
247 | ||
3b52ce44 | 248 | pt = kzalloc(sizeof(*pt), GFP_KERNEL); |
aff9da10 GP |
249 | if (!pt) |
250 | return NULL; | |
251 | ||
aff9da10 | 252 | sync_timeline_get(obj); |
d3862e44 | 253 | dma_fence_init(&pt->base, &timeline_fence_ops, &obj->lock, |
f54d1867 | 254 | obj->context, value); |
d3862e44 | 255 | INIT_LIST_HEAD(&pt->link); |
a6aa8fca | 256 | |
d3862e44 | 257 | spin_lock_irq(&obj->lock); |
f1e8c671 CW |
258 | if (!dma_fence_is_signaled_locked(&pt->base)) { |
259 | struct rb_node **p = &obj->pt_tree.rb_node; | |
260 | struct rb_node *parent = NULL; | |
261 | ||
262 | while (*p) { | |
263 | struct sync_pt *other; | |
264 | int cmp; | |
265 | ||
266 | parent = *p; | |
267 | other = rb_entry(parent, typeof(*pt), node); | |
268 | cmp = value - other->base.seqno; | |
269 | if (cmp > 0) { | |
270 | p = &parent->rb_right; | |
271 | } else if (cmp < 0) { | |
272 | p = &parent->rb_left; | |
273 | } else { | |
274 | if (dma_fence_get_rcu(&other->base)) { | |
e66cc217 CW |
275 | sync_timeline_put(obj); |
276 | kfree(pt); | |
f1e8c671 CW |
277 | pt = other; |
278 | goto unlock; | |
279 | } | |
280 | p = &parent->rb_left; | |
281 | } | |
282 | } | |
283 | rb_link_node(&pt->node, parent, p); | |
284 | rb_insert_color(&pt->node, &obj->pt_tree); | |
285 | ||
286 | parent = rb_next(&pt->node); | |
287 | list_add_tail(&pt->link, | |
288 | parent ? &rb_entry(parent, typeof(*pt), node)->link : &obj->pt_list); | |
289 | } | |
290 | unlock: | |
d3862e44 | 291 | spin_unlock_irq(&obj->lock); |
a6aa8fca | 292 | |
aff9da10 GP |
293 | return pt; |
294 | } | |
295 | ||
1867a23b GP |
296 | /* |
297 | * *WARNING* | |
298 | * | |
299 | * improper use of this can result in deadlocking kernel drivers from userspace. | |
300 | */ | |
301 | ||
302 | /* opening sw_sync create a new sync obj */ | |
303 | static int sw_sync_debugfs_open(struct inode *inode, struct file *file) | |
304 | { | |
305 | struct sync_timeline *obj; | |
306 | char task_comm[TASK_COMM_LEN]; | |
307 | ||
308 | get_task_comm(task_comm, current); | |
309 | ||
b9bc2b7b | 310 | obj = sync_timeline_create(task_comm); |
1867a23b GP |
311 | if (!obj) |
312 | return -ENOMEM; | |
313 | ||
314 | file->private_data = obj; | |
315 | ||
316 | return 0; | |
317 | } | |
318 | ||
319 | static int sw_sync_debugfs_release(struct inode *inode, struct file *file) | |
320 | { | |
321 | struct sync_timeline *obj = file->private_data; | |
ea4d5a27 DB |
322 | struct sync_pt *pt, *next; |
323 | ||
324 | spin_lock_irq(&obj->lock); | |
325 | ||
326 | list_for_each_entry_safe(pt, next, &obj->pt_list, link) { | |
327 | dma_fence_set_error(&pt->base, -ENOENT); | |
328 | dma_fence_signal_locked(&pt->base); | |
329 | } | |
1867a23b | 330 | |
ea4d5a27 | 331 | spin_unlock_irq(&obj->lock); |
71110232 GP |
332 | |
333 | sync_timeline_put(obj); | |
1867a23b GP |
334 | return 0; |
335 | } | |
336 | ||
337 | static long sw_sync_ioctl_create_fence(struct sync_timeline *obj, | |
338 | unsigned long arg) | |
339 | { | |
340 | int fd = get_unused_fd_flags(O_CLOEXEC); | |
341 | int err; | |
342 | struct sync_pt *pt; | |
343 | struct sync_file *sync_file; | |
344 | struct sw_sync_create_fence_data data; | |
345 | ||
346 | if (fd < 0) | |
347 | return fd; | |
348 | ||
349 | if (copy_from_user(&data, (void __user *)arg, sizeof(data))) { | |
350 | err = -EFAULT; | |
351 | goto err; | |
352 | } | |
353 | ||
3b52ce44 | 354 | pt = sync_pt_create(obj, data.value); |
1867a23b GP |
355 | if (!pt) { |
356 | err = -ENOMEM; | |
357 | goto err; | |
358 | } | |
359 | ||
360 | sync_file = sync_file_create(&pt->base); | |
4592bfcd | 361 | dma_fence_put(&pt->base); |
1867a23b | 362 | if (!sync_file) { |
1867a23b GP |
363 | err = -ENOMEM; |
364 | goto err; | |
365 | } | |
366 | ||
367 | data.fence = fd; | |
368 | if (copy_to_user((void __user *)arg, &data, sizeof(data))) { | |
369 | fput(sync_file->file); | |
370 | err = -EFAULT; | |
371 | goto err; | |
372 | } | |
373 | ||
374 | fd_install(fd, sync_file->file); | |
375 | ||
376 | return 0; | |
377 | ||
378 | err: | |
379 | put_unused_fd(fd); | |
380 | return err; | |
381 | } | |
382 | ||
383 | static long sw_sync_ioctl_inc(struct sync_timeline *obj, unsigned long arg) | |
384 | { | |
385 | u32 value; | |
386 | ||
387 | if (copy_from_user(&value, (void __user *)arg, sizeof(value))) | |
388 | return -EFAULT; | |
389 | ||
8f66d3aa CW |
390 | while (value > INT_MAX) { |
391 | sync_timeline_signal(obj, INT_MAX); | |
392 | value -= INT_MAX; | |
393 | } | |
394 | ||
1867a23b GP |
395 | sync_timeline_signal(obj, value); |
396 | ||
397 | return 0; | |
398 | } | |
399 | ||
400 | static long sw_sync_ioctl(struct file *file, unsigned int cmd, | |
401 | unsigned long arg) | |
402 | { | |
403 | struct sync_timeline *obj = file->private_data; | |
404 | ||
405 | switch (cmd) { | |
406 | case SW_SYNC_IOC_CREATE_FENCE: | |
407 | return sw_sync_ioctl_create_fence(obj, arg); | |
408 | ||
409 | case SW_SYNC_IOC_INC: | |
410 | return sw_sync_ioctl_inc(obj, arg); | |
411 | ||
412 | default: | |
413 | return -ENOTTY; | |
414 | } | |
415 | } | |
416 | ||
417 | const struct file_operations sw_sync_debugfs_fops = { | |
418 | .open = sw_sync_debugfs_open, | |
419 | .release = sw_sync_debugfs_release, | |
420 | .unlocked_ioctl = sw_sync_ioctl, | |
421 | .compat_ioctl = sw_sync_ioctl, | |
422 | }; |