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