sync_debug.c revision 954513551f5afb7893b7c688a67219356ab3efe6
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#include "sync.h"
29
30#ifdef CONFIG_DEBUG_FS
31
32static LIST_HEAD(sync_timeline_list_head);
33static DEFINE_SPINLOCK(sync_timeline_list_lock);
34static LIST_HEAD(sync_fence_list_head);
35static DEFINE_SPINLOCK(sync_fence_list_lock);
36
37void sync_timeline_debug_add(struct sync_timeline *obj)
38{
39	unsigned long flags;
40
41	spin_lock_irqsave(&sync_timeline_list_lock, flags);
42	list_add_tail(&obj->sync_timeline_list, &sync_timeline_list_head);
43	spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
44}
45
46void sync_timeline_debug_remove(struct sync_timeline *obj)
47{
48	unsigned long flags;
49
50	spin_lock_irqsave(&sync_timeline_list_lock, flags);
51	list_del(&obj->sync_timeline_list);
52	spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
53}
54
55void sync_fence_debug_add(struct sync_fence *fence)
56{
57	unsigned long flags;
58
59	spin_lock_irqsave(&sync_fence_list_lock, flags);
60	list_add_tail(&fence->sync_fence_list, &sync_fence_list_head);
61	spin_unlock_irqrestore(&sync_fence_list_lock, flags);
62}
63
64void sync_fence_debug_remove(struct sync_fence *fence)
65{
66	unsigned long flags;
67
68	spin_lock_irqsave(&sync_fence_list_lock, flags);
69	list_del(&fence->sync_fence_list);
70	spin_unlock_irqrestore(&sync_fence_list_lock, flags);
71}
72
73static const char *sync_status_str(int status)
74{
75	if (status == 0)
76		return "signaled";
77
78	if (status > 0)
79		return "active";
80
81	return "error";
82}
83
84static void sync_print_pt(struct seq_file *s, struct sync_pt *pt, bool fence)
85{
86	int status = 1;
87	struct sync_timeline *parent = sync_pt_parent(pt);
88
89	if (fence_is_signaled_locked(&pt->base))
90		status = pt->base.status;
91
92	seq_printf(s, "  %s%spt %s",
93		   fence ? parent->name : "",
94		   fence ? "_" : "",
95		   sync_status_str(status));
96
97	if (status <= 0) {
98		struct timeval tv = ktime_to_timeval(pt->base.timestamp);
99
100		seq_printf(s, "@%ld.%06ld", tv.tv_sec, tv.tv_usec);
101	}
102
103	if (parent->ops->timeline_value_str &&
104	    parent->ops->pt_value_str) {
105		char value[64];
106
107		parent->ops->pt_value_str(pt, value, sizeof(value));
108		seq_printf(s, ": %s", value);
109		if (fence) {
110			parent->ops->timeline_value_str(parent, value,
111						    sizeof(value));
112			seq_printf(s, " / %s", value);
113		}
114	}
115
116	seq_puts(s, "\n");
117}
118
119static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj)
120{
121	struct list_head *pos;
122	unsigned long flags;
123
124	seq_printf(s, "%s %s", obj->name, obj->ops->driver_name);
125
126	if (obj->ops->timeline_value_str) {
127		char value[64];
128
129		obj->ops->timeline_value_str(obj, value, sizeof(value));
130		seq_printf(s, ": %s", value);
131	}
132
133	seq_puts(s, "\n");
134
135	spin_lock_irqsave(&obj->child_list_lock, flags);
136	list_for_each(pos, &obj->child_list_head) {
137		struct sync_pt *pt =
138			container_of(pos, struct sync_pt, child_list);
139		sync_print_pt(s, pt, false);
140	}
141	spin_unlock_irqrestore(&obj->child_list_lock, flags);
142}
143
144static void sync_print_fence(struct seq_file *s, struct sync_fence *fence)
145{
146	wait_queue_t *pos;
147	unsigned long flags;
148	int i;
149
150	seq_printf(s, "[%p] %s: %s\n", fence, fence->name,
151		   sync_status_str(atomic_read(&fence->status)));
152
153	for (i = 0; i < fence->num_fences; ++i) {
154		struct sync_pt *pt =
155			container_of(fence->cbs[i].sync_pt,
156				     struct sync_pt, base);
157
158		sync_print_pt(s, pt, true);
159	}
160
161	spin_lock_irqsave(&fence->wq.lock, flags);
162	list_for_each_entry(pos, &fence->wq.task_list, task_list) {
163		struct sync_fence_waiter *waiter;
164
165		if (pos->func != &sync_fence_wake_up_wq)
166			continue;
167
168		waiter = container_of(pos, struct sync_fence_waiter, work);
169
170		seq_printf(s, "waiter %pF\n", waiter->callback);
171	}
172	spin_unlock_irqrestore(&fence->wq.lock, flags);
173}
174
175static int sync_debugfs_show(struct seq_file *s, void *unused)
176{
177	unsigned long flags;
178	struct list_head *pos;
179
180	seq_puts(s, "objs:\n--------------\n");
181
182	spin_lock_irqsave(&sync_timeline_list_lock, flags);
183	list_for_each(pos, &sync_timeline_list_head) {
184		struct sync_timeline *obj =
185			container_of(pos, struct sync_timeline,
186				     sync_timeline_list);
187
188		sync_print_obj(s, obj);
189		seq_puts(s, "\n");
190	}
191	spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
192
193	seq_puts(s, "fences:\n--------------\n");
194
195	spin_lock_irqsave(&sync_fence_list_lock, flags);
196	list_for_each(pos, &sync_fence_list_head) {
197		struct sync_fence *fence =
198			container_of(pos, struct sync_fence, sync_fence_list);
199
200		sync_print_fence(s, fence);
201		seq_puts(s, "\n");
202	}
203	spin_unlock_irqrestore(&sync_fence_list_lock, flags);
204	return 0;
205}
206
207static int sync_debugfs_open(struct inode *inode, struct file *file)
208{
209	return single_open(file, sync_debugfs_show, inode->i_private);
210}
211
212static const struct file_operations sync_debugfs_fops = {
213	.open           = sync_debugfs_open,
214	.read           = seq_read,
215	.llseek         = seq_lseek,
216	.release        = single_release,
217};
218
219static __init int sync_debugfs_init(void)
220{
221	debugfs_create_file("sync", S_IRUGO, NULL, NULL, &sync_debugfs_fops);
222	return 0;
223}
224late_initcall(sync_debugfs_init);
225
226#define DUMP_CHUNK 256
227static char sync_dump_buf[64 * 1024];
228void sync_dump(void)
229{
230	struct seq_file s = {
231		.buf = sync_dump_buf,
232		.size = sizeof(sync_dump_buf) - 1,
233	};
234	int i;
235
236	sync_debugfs_show(&s, NULL);
237
238	for (i = 0; i < s.count; i += DUMP_CHUNK) {
239		if ((s.count - i) > DUMP_CHUNK) {
240			char c = s.buf[i + DUMP_CHUNK];
241
242			s.buf[i + DUMP_CHUNK] = 0;
243			pr_cont("%s", s.buf + i);
244			s.buf[i + DUMP_CHUNK] = c;
245		} else {
246			s.buf[s.count] = 0;
247			pr_cont("%s", s.buf + i);
248		}
249	}
250}
251
252#endif
253