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