blob: 13b26d21cea03c75279781cc1ea00bc6f1716a87 [file] [log] [blame]
Erik Gilling010accf2012-03-13 15:34:34 -07001/*
2 * drivers/base/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
Erik Gilling981c8a92012-03-14 19:49:15 -070017#include <linux/debugfs.h>
Erik Gilling4fb837a2012-05-16 13:09:22 -070018#include <linux/export.h>
Erik Gilling010accf2012-03-13 15:34:34 -070019#include <linux/file.h>
20#include <linux/fs.h>
21#include <linux/kernel.h>
Erik Gillinga1eeaca2012-03-19 17:28:32 -070022#include <linux/poll.h>
Erik Gilling010accf2012-03-13 15:34:34 -070023#include <linux/sched.h>
Erik Gilling981c8a92012-03-14 19:49:15 -070024#include <linux/seq_file.h>
Erik Gilling010accf2012-03-13 15:34:34 -070025#include <linux/slab.h>
26#include <linux/sync.h>
27#include <linux/uaccess.h>
28
29#include <linux/anon_inodes.h>
30
31static void sync_fence_signal_pt(struct sync_pt *pt);
32static int _sync_pt_has_signaled(struct sync_pt *pt);
33
Erik Gilling981c8a92012-03-14 19:49:15 -070034static LIST_HEAD(sync_timeline_list_head);
35static DEFINE_SPINLOCK(sync_timeline_list_lock);
36
37static LIST_HEAD(sync_fence_list_head);
38static DEFINE_SPINLOCK(sync_fence_list_lock);
39
Erik Gilling010accf2012-03-13 15:34:34 -070040struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops,
41 int size, const char *name)
42{
43 struct sync_timeline *obj;
Erik Gilling981c8a92012-03-14 19:49:15 -070044 unsigned long flags;
Erik Gilling010accf2012-03-13 15:34:34 -070045
46 if (size < sizeof(struct sync_timeline))
47 return NULL;
48
49 obj = kzalloc(size, GFP_KERNEL);
50 if (obj == NULL)
51 return NULL;
52
53 obj->ops = ops;
54 strlcpy(obj->name, name, sizeof(obj->name));
55
56 INIT_LIST_HEAD(&obj->child_list_head);
57 spin_lock_init(&obj->child_list_lock);
58
59 INIT_LIST_HEAD(&obj->active_list_head);
60 spin_lock_init(&obj->active_list_lock);
61
Erik Gilling981c8a92012-03-14 19:49:15 -070062 spin_lock_irqsave(&sync_timeline_list_lock, flags);
63 list_add_tail(&obj->sync_timeline_list, &sync_timeline_list_head);
64 spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
65
Erik Gilling010accf2012-03-13 15:34:34 -070066 return obj;
67}
Erik Gilling4fb837a2012-05-16 13:09:22 -070068EXPORT_SYMBOL(sync_timeline_create);
Erik Gilling010accf2012-03-13 15:34:34 -070069
Erik Gilling981c8a92012-03-14 19:49:15 -070070static void sync_timeline_free(struct sync_timeline *obj)
71{
72 unsigned long flags;
73
74 if (obj->ops->release_obj)
75 obj->ops->release_obj(obj);
76
77 spin_lock_irqsave(&sync_timeline_list_lock, flags);
78 list_del(&obj->sync_timeline_list);
79 spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
80
81 kfree(obj);
82}
83
Erik Gilling010accf2012-03-13 15:34:34 -070084void sync_timeline_destroy(struct sync_timeline *obj)
85{
86 unsigned long flags;
87 bool needs_freeing;
88
89 spin_lock_irqsave(&obj->child_list_lock, flags);
90 obj->destroyed = true;
91 needs_freeing = list_empty(&obj->child_list_head);
92 spin_unlock_irqrestore(&obj->child_list_lock, flags);
93
94 if (needs_freeing)
Erik Gilling981c8a92012-03-14 19:49:15 -070095 sync_timeline_free(obj);
Erik Gilling010accf2012-03-13 15:34:34 -070096 else
97 sync_timeline_signal(obj);
98}
Erik Gilling4fb837a2012-05-16 13:09:22 -070099EXPORT_SYMBOL(sync_timeline_destroy);
Erik Gilling010accf2012-03-13 15:34:34 -0700100
101static void sync_timeline_add_pt(struct sync_timeline *obj, struct sync_pt *pt)
102{
103 unsigned long flags;
104
105 pt->parent = obj;
106
107 spin_lock_irqsave(&obj->child_list_lock, flags);
108 list_add_tail(&pt->child_list, &obj->child_list_head);
109 spin_unlock_irqrestore(&obj->child_list_lock, flags);
110}
111
112static void sync_timeline_remove_pt(struct sync_pt *pt)
113{
114 struct sync_timeline *obj = pt->parent;
115 unsigned long flags;
116 bool needs_freeing;
117
118 spin_lock_irqsave(&obj->active_list_lock, flags);
119 if (!list_empty(&pt->active_list))
120 list_del_init(&pt->active_list);
121 spin_unlock_irqrestore(&obj->active_list_lock, flags);
122
123 spin_lock_irqsave(&obj->child_list_lock, flags);
124 list_del(&pt->child_list);
125 needs_freeing = obj->destroyed && list_empty(&obj->child_list_head);
126 spin_unlock_irqrestore(&obj->child_list_lock, flags);
127
128 if (needs_freeing)
Erik Gilling981c8a92012-03-14 19:49:15 -0700129 sync_timeline_free(obj);
Erik Gilling010accf2012-03-13 15:34:34 -0700130}
131
132void sync_timeline_signal(struct sync_timeline *obj)
133{
134 unsigned long flags;
135 LIST_HEAD(signaled_pts);
136 struct list_head *pos, *n;
137
138 spin_lock_irqsave(&obj->active_list_lock, flags);
139
140 list_for_each_safe(pos, n, &obj->active_list_head) {
141 struct sync_pt *pt =
142 container_of(pos, struct sync_pt, active_list);
143
144 if (_sync_pt_has_signaled(pt))
145 list_move(pos, &signaled_pts);
146 }
147
148 spin_unlock_irqrestore(&obj->active_list_lock, flags);
149
150 list_for_each_safe(pos, n, &signaled_pts) {
151 struct sync_pt *pt =
152 container_of(pos, struct sync_pt, active_list);
153
154 list_del_init(pos);
155 sync_fence_signal_pt(pt);
156 }
157}
Erik Gilling4fb837a2012-05-16 13:09:22 -0700158EXPORT_SYMBOL(sync_timeline_signal);
Erik Gilling010accf2012-03-13 15:34:34 -0700159
160struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size)
161{
162 struct sync_pt *pt;
163
164 if (size < sizeof(struct sync_pt))
165 return NULL;
166
167 pt = kzalloc(size, GFP_KERNEL);
168 if (pt == NULL)
169 return NULL;
170
171 INIT_LIST_HEAD(&pt->active_list);
172 sync_timeline_add_pt(parent, pt);
173
174 return pt;
175}
Erik Gilling4fb837a2012-05-16 13:09:22 -0700176EXPORT_SYMBOL(sync_pt_create);
Erik Gilling010accf2012-03-13 15:34:34 -0700177
178void sync_pt_free(struct sync_pt *pt)
179{
180 if (pt->parent->ops->free_pt)
181 pt->parent->ops->free_pt(pt);
182
183 sync_timeline_remove_pt(pt);
184
185 kfree(pt);
186}
Erik Gilling4fb837a2012-05-16 13:09:22 -0700187EXPORT_SYMBOL(sync_pt_free);
Erik Gilling010accf2012-03-13 15:34:34 -0700188
189/* call with pt->parent->active_list_lock held */
190static int _sync_pt_has_signaled(struct sync_pt *pt)
191{
Erik Gillingad433ba2012-03-15 14:59:33 -0700192 int old_status = pt->status;
193
Erik Gilling010accf2012-03-13 15:34:34 -0700194 if (!pt->status)
195 pt->status = pt->parent->ops->has_signaled(pt);
196
197 if (!pt->status && pt->parent->destroyed)
198 pt->status = -ENOENT;
199
Erik Gillingad433ba2012-03-15 14:59:33 -0700200 if (pt->status != old_status)
201 pt->timestamp = ktime_get();
202
Erik Gilling010accf2012-03-13 15:34:34 -0700203 return pt->status;
204}
205
206static struct sync_pt *sync_pt_dup(struct sync_pt *pt)
207{
208 return pt->parent->ops->dup(pt);
209}
210
211/* Adds a sync pt to the active queue. Called when added to a fence */
212static void sync_pt_activate(struct sync_pt *pt)
213{
214 struct sync_timeline *obj = pt->parent;
215 unsigned long flags;
216 int err;
217
218 spin_lock_irqsave(&obj->active_list_lock, flags);
219
220 err = _sync_pt_has_signaled(pt);
221 if (err != 0)
222 goto out;
223
224 list_add_tail(&pt->active_list, &obj->active_list_head);
225
226out:
227 spin_unlock_irqrestore(&obj->active_list_lock, flags);
228}
229
230static int sync_fence_release(struct inode *inode, struct file *file);
Erik Gillinga1eeaca2012-03-19 17:28:32 -0700231static unsigned int sync_fence_poll(struct file *file, poll_table *wait);
Erik Gilling010accf2012-03-13 15:34:34 -0700232static long sync_fence_ioctl(struct file *file, unsigned int cmd,
233 unsigned long arg);
234
235
236static const struct file_operations sync_fence_fops = {
237 .release = sync_fence_release,
Erik Gillinga1eeaca2012-03-19 17:28:32 -0700238 .poll = sync_fence_poll,
Erik Gilling010accf2012-03-13 15:34:34 -0700239 .unlocked_ioctl = sync_fence_ioctl,
240};
241
242static struct sync_fence *sync_fence_alloc(const char *name)
243{
244 struct sync_fence *fence;
Erik Gilling981c8a92012-03-14 19:49:15 -0700245 unsigned long flags;
Erik Gilling010accf2012-03-13 15:34:34 -0700246
247 fence = kzalloc(sizeof(struct sync_fence), GFP_KERNEL);
248 if (fence == NULL)
249 return NULL;
250
251 fence->file = anon_inode_getfile("sync_fence", &sync_fence_fops,
252 fence, 0);
253 if (fence->file == NULL)
254 goto err;
255
256 strlcpy(fence->name, name, sizeof(fence->name));
257
258 INIT_LIST_HEAD(&fence->pt_list_head);
259 INIT_LIST_HEAD(&fence->waiter_list_head);
260 spin_lock_init(&fence->waiter_list_lock);
261
262 init_waitqueue_head(&fence->wq);
Erik Gilling981c8a92012-03-14 19:49:15 -0700263
264 spin_lock_irqsave(&sync_fence_list_lock, flags);
265 list_add_tail(&fence->sync_fence_list, &sync_fence_list_head);
266 spin_unlock_irqrestore(&sync_fence_list_lock, flags);
267
Erik Gilling010accf2012-03-13 15:34:34 -0700268 return fence;
269
270err:
271 kfree(fence);
272 return NULL;
273}
274
275/* TODO: implement a create which takes more that one sync_pt */
276struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt)
277{
278 struct sync_fence *fence;
279
280 if (pt->fence)
281 return NULL;
282
283 fence = sync_fence_alloc(name);
284 if (fence == NULL)
285 return NULL;
286
287 pt->fence = fence;
288 list_add(&pt->pt_list, &fence->pt_list_head);
289 sync_pt_activate(pt);
290
291 return fence;
292}
Erik Gilling4fb837a2012-05-16 13:09:22 -0700293EXPORT_SYMBOL(sync_fence_create);
Erik Gilling010accf2012-03-13 15:34:34 -0700294
295static int sync_fence_copy_pts(struct sync_fence *dst, struct sync_fence *src)
296{
297 struct list_head *pos;
298
299 list_for_each(pos, &src->pt_list_head) {
300 struct sync_pt *orig_pt =
301 container_of(pos, struct sync_pt, pt_list);
302 struct sync_pt *new_pt = sync_pt_dup(orig_pt);
303
304 if (new_pt == NULL)
305 return -ENOMEM;
306
307 new_pt->fence = dst;
308 list_add(&new_pt->pt_list, &dst->pt_list_head);
309 sync_pt_activate(new_pt);
310 }
311
312 return 0;
313}
314
Ajay Dudani99343192012-07-11 17:13:50 -0700315static int sync_fence_merge_pts(struct sync_fence *dst, struct sync_fence *src)
316{
317 struct list_head *src_pos, *dst_pos, *n;
318
319 list_for_each(src_pos, &src->pt_list_head) {
320 struct sync_pt *src_pt =
321 container_of(src_pos, struct sync_pt, pt_list);
322 bool collapsed = false;
323
324 list_for_each_safe(dst_pos, n, &dst->pt_list_head) {
325 struct sync_pt *dst_pt =
326 container_of(dst_pos, struct sync_pt, pt_list);
327 /* collapse two sync_pts on the same timeline
328 * to a single sync_pt that will signal at
329 * the later of the two
330 */
331 if (dst_pt->parent == src_pt->parent) {
332 if (dst_pt->parent->ops->compare(dst_pt, src_pt) == -1) {
333 struct sync_pt *new_pt =
334 sync_pt_dup(src_pt);
335 if (new_pt == NULL)
336 return -ENOMEM;
337
338 new_pt->fence = dst;
339 list_replace(&dst_pt->pt_list,
340 &new_pt->pt_list);
341 sync_pt_activate(new_pt);
342 sync_pt_free(dst_pt);
343 }
344 collapsed = true;
345 break;
346 }
347 }
348
349 if (!collapsed) {
350 struct sync_pt *new_pt = sync_pt_dup(src_pt);
351
352 if (new_pt == NULL)
353 return -ENOMEM;
354
355 new_pt->fence = dst;
356 list_add(&new_pt->pt_list, &dst->pt_list_head);
357 sync_pt_activate(new_pt);
358 }
359 }
360
361 return 0;
362}
363
Erik Gilling010accf2012-03-13 15:34:34 -0700364static void sync_fence_free_pts(struct sync_fence *fence)
365{
366 struct list_head *pos, *n;
367
368 list_for_each_safe(pos, n, &fence->pt_list_head) {
369 struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list);
370 sync_pt_free(pt);
371 }
372}
373
374struct sync_fence *sync_fence_fdget(int fd)
375{
376 struct file *file = fget(fd);
377
378 if (file == NULL)
379 return NULL;
380
381 if (file->f_op != &sync_fence_fops)
382 goto err;
383
384 return file->private_data;
385
386err:
387 fput(file);
388 return NULL;
389}
Erik Gilling4fb837a2012-05-16 13:09:22 -0700390EXPORT_SYMBOL(sync_fence_fdget);
Erik Gilling010accf2012-03-13 15:34:34 -0700391
392void sync_fence_put(struct sync_fence *fence)
393{
394 fput(fence->file);
395}
Erik Gilling4fb837a2012-05-16 13:09:22 -0700396EXPORT_SYMBOL(sync_fence_put);
Erik Gilling010accf2012-03-13 15:34:34 -0700397
398void sync_fence_install(struct sync_fence *fence, int fd)
399{
400 fd_install(fd, fence->file);
401}
Erik Gilling4fb837a2012-05-16 13:09:22 -0700402EXPORT_SYMBOL(sync_fence_install);
Erik Gilling010accf2012-03-13 15:34:34 -0700403
404static int sync_fence_get_status(struct sync_fence *fence)
405{
406 struct list_head *pos;
407 int status = 1;
408
409 list_for_each(pos, &fence->pt_list_head) {
410 struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list);
411 int pt_status = pt->status;
412
413 if (pt_status < 0) {
414 status = pt_status;
415 break;
416 } else if (status == 1) {
417 status = pt_status;
418 }
419 }
420
421 return status;
422}
423
424struct sync_fence *sync_fence_merge(const char *name,
425 struct sync_fence *a, struct sync_fence *b)
426{
427 struct sync_fence *fence;
428 int err;
429
430 fence = sync_fence_alloc(name);
431 if (fence == NULL)
432 return NULL;
433
434 err = sync_fence_copy_pts(fence, a);
435 if (err < 0)
436 goto err;
437
Ajay Dudani99343192012-07-11 17:13:50 -0700438 err = sync_fence_merge_pts(fence, b);
Erik Gilling010accf2012-03-13 15:34:34 -0700439 if (err < 0)
440 goto err;
441
442 fence->status = sync_fence_get_status(fence);
443
444 return fence;
445err:
446 sync_fence_free_pts(fence);
447 kfree(fence);
448 return NULL;
449}
Erik Gilling4fb837a2012-05-16 13:09:22 -0700450EXPORT_SYMBOL(sync_fence_merge);
Erik Gilling010accf2012-03-13 15:34:34 -0700451
452static void sync_fence_signal_pt(struct sync_pt *pt)
453{
454 LIST_HEAD(signaled_waiters);
455 struct sync_fence *fence = pt->fence;
456 struct list_head *pos;
457 struct list_head *n;
458 unsigned long flags;
459 int status;
460
461 status = sync_fence_get_status(fence);
462
463 spin_lock_irqsave(&fence->waiter_list_lock, flags);
464 /*
465 * this should protect against two threads racing on the signaled
466 * false -> true transition
467 */
468 if (status && !fence->status) {
469 list_for_each_safe(pos, n, &fence->waiter_list_head)
470 list_move(pos, &signaled_waiters);
471
472 fence->status = status;
473 } else {
474 status = 0;
475 }
476 spin_unlock_irqrestore(&fence->waiter_list_lock, flags);
477
478 if (status) {
479 list_for_each_safe(pos, n, &signaled_waiters) {
480 struct sync_fence_waiter *waiter =
481 container_of(pos, struct sync_fence_waiter,
482 waiter_list);
483
Erik Gilling010accf2012-03-13 15:34:34 -0700484 list_del(pos);
Erik Gillingc80114f2012-05-15 16:23:26 -0700485 waiter->callback(fence, waiter);
Erik Gilling010accf2012-03-13 15:34:34 -0700486 }
487 wake_up(&fence->wq);
488 }
489}
490
491int sync_fence_wait_async(struct sync_fence *fence,
Erik Gillingc80114f2012-05-15 16:23:26 -0700492 struct sync_fence_waiter *waiter)
Erik Gilling010accf2012-03-13 15:34:34 -0700493{
Erik Gilling010accf2012-03-13 15:34:34 -0700494 unsigned long flags;
495 int err = 0;
496
Erik Gilling010accf2012-03-13 15:34:34 -0700497 spin_lock_irqsave(&fence->waiter_list_lock, flags);
498
499 if (fence->status) {
Erik Gilling010accf2012-03-13 15:34:34 -0700500 err = fence->status;
501 goto out;
502 }
503
504 list_add_tail(&waiter->waiter_list, &fence->waiter_list_head);
505out:
506 spin_unlock_irqrestore(&fence->waiter_list_lock, flags);
507
508 return err;
509}
Erik Gilling4fb837a2012-05-16 13:09:22 -0700510EXPORT_SYMBOL(sync_fence_wait_async);
Erik Gilling010accf2012-03-13 15:34:34 -0700511
Erik Gillingc80114f2012-05-15 16:23:26 -0700512int sync_fence_cancel_async(struct sync_fence *fence,
513 struct sync_fence_waiter *waiter)
514{
515 struct list_head *pos;
516 struct list_head *n;
517 unsigned long flags;
518 int ret = -ENOENT;
519
520 spin_lock_irqsave(&fence->waiter_list_lock, flags);
521 /*
522 * Make sure waiter is still in waiter_list because it is possible for
523 * the waiter to be removed from the list while the callback is still
524 * pending.
525 */
526 list_for_each_safe(pos, n, &fence->waiter_list_head) {
527 struct sync_fence_waiter *list_waiter =
528 container_of(pos, struct sync_fence_waiter,
529 waiter_list);
530 if (list_waiter == waiter) {
531 list_del(pos);
532 ret = 0;
533 break;
534 }
535 }
536 spin_unlock_irqrestore(&fence->waiter_list_lock, flags);
537 return ret;
538}
Erik Gilling4fb837a2012-05-16 13:09:22 -0700539EXPORT_SYMBOL(sync_fence_cancel_async);
Erik Gillingc80114f2012-05-15 16:23:26 -0700540
Erik Gilling010accf2012-03-13 15:34:34 -0700541int sync_fence_wait(struct sync_fence *fence, long timeout)
542{
543 int err;
544
545 if (timeout) {
546 timeout = msecs_to_jiffies(timeout);
547 err = wait_event_interruptible_timeout(fence->wq,
548 fence->status != 0,
549 timeout);
550 } else {
551 err = wait_event_interruptible(fence->wq, fence->status != 0);
552 }
553
554 if (err < 0)
555 return err;
556
557 if (fence->status < 0)
558 return fence->status;
559
560 if (fence->status == 0)
561 return -ETIME;
562
563 return 0;
564}
Erik Gilling4fb837a2012-05-16 13:09:22 -0700565EXPORT_SYMBOL(sync_fence_wait);
Erik Gilling010accf2012-03-13 15:34:34 -0700566
567static int sync_fence_release(struct inode *inode, struct file *file)
568{
569 struct sync_fence *fence = file->private_data;
Erik Gilling981c8a92012-03-14 19:49:15 -0700570 unsigned long flags;
Erik Gilling010accf2012-03-13 15:34:34 -0700571
Erik Gilling981c8a92012-03-14 19:49:15 -0700572 spin_lock_irqsave(&sync_fence_list_lock, flags);
573 list_del(&fence->sync_fence_list);
574 spin_unlock_irqrestore(&sync_fence_list_lock, flags);
575
Ajay Dudani1ee76852012-07-11 17:07:39 -0700576 sync_fence_free_pts(fence);
577
Erik Gilling010accf2012-03-13 15:34:34 -0700578 kfree(fence);
579
580 return 0;
581}
582
Erik Gillinga1eeaca2012-03-19 17:28:32 -0700583static unsigned int sync_fence_poll(struct file *file, poll_table *wait)
584{
585 struct sync_fence *fence = file->private_data;
586
587 poll_wait(file, &fence->wq, wait);
588
589 if (fence->status == 1)
590 return POLLIN;
591 else if (fence->status < 0)
592 return POLLERR;
593 else
594 return 0;
595}
596
Erik Gilling010accf2012-03-13 15:34:34 -0700597static long sync_fence_ioctl_wait(struct sync_fence *fence, unsigned long arg)
598{
599 __u32 value;
600
601 if (copy_from_user(&value, (void __user *)arg, sizeof(value)))
602 return -EFAULT;
603
604 return sync_fence_wait(fence, value);
605}
606
607static long sync_fence_ioctl_merge(struct sync_fence *fence, unsigned long arg)
608{
609 int fd = get_unused_fd();
610 int err;
611 struct sync_fence *fence2, *fence3;
612 struct sync_merge_data data;
613
614 if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
615 return -EFAULT;
616
617 fence2 = sync_fence_fdget(data.fd2);
618 if (fence2 == NULL) {
619 err = -ENOENT;
620 goto err_put_fd;
621 }
622
623 data.name[sizeof(data.name) - 1] = '\0';
624 fence3 = sync_fence_merge(data.name, fence, fence2);
625 if (fence3 == NULL) {
626 err = -ENOMEM;
627 goto err_put_fence2;
628 }
629
630 data.fence = fd;
631 if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
632 err = -EFAULT;
633 goto err_put_fence3;
634 }
635
636 sync_fence_install(fence3, fd);
637 sync_fence_put(fence2);
638 return 0;
639
640err_put_fence3:
641 sync_fence_put(fence3);
642
643err_put_fence2:
644 sync_fence_put(fence2);
645
646err_put_fd:
647 put_unused_fd(fd);
648 return err;
649}
650
Erik Gilling3913bff2012-03-15 17:45:50 -0700651static int sync_fill_pt_info(struct sync_pt *pt, void *data, int size)
652{
653 struct sync_pt_info *info = data;
654 int ret;
655
656 if (size < sizeof(struct sync_pt_info))
657 return -ENOMEM;
658
659 info->len = sizeof(struct sync_pt_info);
660
661 if (pt->parent->ops->fill_driver_data) {
662 ret = pt->parent->ops->fill_driver_data(pt, info->driver_data,
663 size - sizeof(*info));
664 if (ret < 0)
665 return ret;
666
667 info->len += ret;
668 }
669
670 strlcpy(info->obj_name, pt->parent->name, sizeof(info->obj_name));
671 strlcpy(info->driver_name, pt->parent->ops->driver_name,
672 sizeof(info->driver_name));
673 info->status = pt->status;
674 info->timestamp_ns = ktime_to_ns(pt->timestamp);
675
676 return info->len;
677}
678
679static long sync_fence_ioctl_fence_info(struct sync_fence *fence,
680 unsigned long arg)
681{
682 struct sync_fence_info_data *data;
683 struct list_head *pos;
684 __u32 size;
685 __u32 len = 0;
686 int ret;
687
688 if (copy_from_user(&size, (void __user *)arg, sizeof(size)))
689 return -EFAULT;
690
691 if (size < sizeof(struct sync_fence_info_data))
692 return -EINVAL;
693
694 if (size > 4096)
695 size = 4096;
696
697 data = kzalloc(size, GFP_KERNEL);
698 if (data == NULL)
699 return -ENOMEM;
700
701 strlcpy(data->name, fence->name, sizeof(data->name));
702 data->status = fence->status;
703 len = sizeof(struct sync_fence_info_data);
704
705 list_for_each(pos, &fence->pt_list_head) {
706 struct sync_pt *pt =
707 container_of(pos, struct sync_pt, pt_list);
708
709 ret = sync_fill_pt_info(pt, (u8 *)data + len, size - len);
710
711 if (ret < 0)
712 goto out;
713
714 len += ret;
715 }
716
717 data->len = len;
718
719 if (copy_to_user((void __user *)arg, data, len))
720 ret = -EFAULT;
721 else
722 ret = 0;
723
724out:
725 kfree(data);
726
727 return ret;
728}
Erik Gilling010accf2012-03-13 15:34:34 -0700729
730static long sync_fence_ioctl(struct file *file, unsigned int cmd,
731 unsigned long arg)
732{
733 struct sync_fence *fence = file->private_data;
734 switch (cmd) {
735 case SYNC_IOC_WAIT:
736 return sync_fence_ioctl_wait(fence, arg);
737
738 case SYNC_IOC_MERGE:
739 return sync_fence_ioctl_merge(fence, arg);
Erik Gilling981c8a92012-03-14 19:49:15 -0700740
Erik Gilling3913bff2012-03-15 17:45:50 -0700741 case SYNC_IOC_FENCE_INFO:
742 return sync_fence_ioctl_fence_info(fence, arg);
743
Erik Gilling010accf2012-03-13 15:34:34 -0700744 default:
745 return -ENOTTY;
746 }
747}
748
Erik Gilling981c8a92012-03-14 19:49:15 -0700749#ifdef CONFIG_DEBUG_FS
750static const char *sync_status_str(int status)
751{
752 if (status > 0)
753 return "signaled";
754 else if (status == 0)
755 return "active";
756 else
757 return "error";
758}
759
760static void sync_print_pt(struct seq_file *s, struct sync_pt *pt, bool fence)
761{
762 int status = pt->status;
763 seq_printf(s, " %s%spt %s",
764 fence ? pt->parent->name : "",
765 fence ? "_" : "",
766 sync_status_str(status));
767 if (pt->status) {
768 struct timeval tv = ktime_to_timeval(pt->timestamp);
769 seq_printf(s, "@%ld.%06ld", tv.tv_sec, tv.tv_usec);
770 }
771
772 if (pt->parent->ops->print_pt) {
773 seq_printf(s, ": ");
774 pt->parent->ops->print_pt(s, pt);
775 }
776
777 seq_printf(s, "\n");
778}
779
780static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj)
781{
782 struct list_head *pos;
783 unsigned long flags;
784
785 seq_printf(s, "%s %s", obj->name, obj->ops->driver_name);
786
787 if (obj->ops->print_obj) {
788 seq_printf(s, ": ");
789 obj->ops->print_obj(s, obj);
790 }
791
792 seq_printf(s, "\n");
793
794 spin_lock_irqsave(&obj->child_list_lock, flags);
795 list_for_each(pos, &obj->child_list_head) {
796 struct sync_pt *pt =
797 container_of(pos, struct sync_pt, child_list);
798 sync_print_pt(s, pt, false);
799 }
800 spin_unlock_irqrestore(&obj->child_list_lock, flags);
801}
802
803static void sync_print_fence(struct seq_file *s, struct sync_fence *fence)
804{
805 struct list_head *pos;
806 unsigned long flags;
807
808 seq_printf(s, "%s: %s\n", fence->name, sync_status_str(fence->status));
809
810 list_for_each(pos, &fence->pt_list_head) {
811 struct sync_pt *pt =
812 container_of(pos, struct sync_pt, pt_list);
813 sync_print_pt(s, pt, true);
814 }
815
816 spin_lock_irqsave(&fence->waiter_list_lock, flags);
817 list_for_each(pos, &fence->waiter_list_head) {
818 struct sync_fence_waiter *waiter =
819 container_of(pos, struct sync_fence_waiter,
820 waiter_list);
821
Erik Gillingc80114f2012-05-15 16:23:26 -0700822 seq_printf(s, "waiter %pF\n", waiter->callback);
Erik Gilling981c8a92012-03-14 19:49:15 -0700823 }
824 spin_unlock_irqrestore(&fence->waiter_list_lock, flags);
825}
826
827static int sync_debugfs_show(struct seq_file *s, void *unused)
828{
829 unsigned long flags;
830 struct list_head *pos;
831
832 seq_printf(s, "objs:\n--------------\n");
833
834 spin_lock_irqsave(&sync_timeline_list_lock, flags);
835 list_for_each(pos, &sync_timeline_list_head) {
836 struct sync_timeline *obj =
837 container_of(pos, struct sync_timeline,
838 sync_timeline_list);
839
840 sync_print_obj(s, obj);
841 seq_printf(s, "\n");
842 }
843 spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
844
845 seq_printf(s, "fences:\n--------------\n");
846
847 spin_lock_irqsave(&sync_fence_list_lock, flags);
848 list_for_each(pos, &sync_fence_list_head) {
849 struct sync_fence *fence =
850 container_of(pos, struct sync_fence, sync_fence_list);
851
852 sync_print_fence(s, fence);
853 seq_printf(s, "\n");
854 }
855 spin_unlock_irqrestore(&sync_fence_list_lock, flags);
856 return 0;
857}
858
859static int sync_debugfs_open(struct inode *inode, struct file *file)
860{
861 return single_open(file, sync_debugfs_show, inode->i_private);
862}
863
864static const struct file_operations sync_debugfs_fops = {
865 .open = sync_debugfs_open,
866 .read = seq_read,
867 .llseek = seq_lseek,
868 .release = single_release,
869};
870
871static __init int sync_debugfs_init(void)
872{
873 debugfs_create_file("sync", S_IRUGO, NULL, NULL, &sync_debugfs_fops);
874 return 0;
875}
876
877late_initcall(sync_debugfs_init);
878
879#endif